Search code examples
javacode-duplication

Duplicate code in static methods


I have two readers implemented in JAVA. See below:

public final class ReaderA {
  public ReaderA() {}

  public static int read(final File file) {
    final byte[] data = Files.readAllbytes(file.toPath());
    return read(data);
  }

  public static int read(final byte[] data) {
    // do somethingA
  }

  ...// and some other methods
}


public final class ReaderB {
  public ReaderB() {}

  // this method is exactly the same as in ReaderA
  public static int read(final File file) {
    final byte[] data = Files.readAllbytes(file.toPath());
    return read(data);
  }

  // this is implemented different from the one in ReaderA
  public static int read(final byte[] data) {
    // do somethingB
  }

  ...// and the same other methods as in ReaderA
}

Question. What's the best way to avoid the duplicate code ?

I tried to extract a the duplicated code in a new abstract class Reader and tried to make the read(final byte[] data) abstract and implement it in subclasses ReaderA and ReaderB. It will not work because the methods are static.


Solution

  • Unless you remove the static modifiers from read(byte[]) and create instances, you won't be able to use inheritance to help you.

    static methods do not behave like instance methods and can't be overriden. Instead, both the super class and the sub class will have seperate methods that need to be qualified with the class name. Pulling up read(File) means it always calls read(byte[]) of the super class. You still have to copy read(File) to each sub class to make it use that class's own read(byte[]). The code in Ward's answer shows this, too.

    For reference, read this question and its answers: Are static methods inherited in Java?

    To illustrate: The two read(File) methods you have are not "exactly the same" as you said in your code snippet. They do not both call this.read(data), but instead ReaderA.read(data) and ReaderB.read(data), respectively. See how the read(byte[]) calls are to two entirely different, non-overridable methods.


    If it is in your power, I would recommend re-writing the readers in a less static way:

    interface Reader
    {
        default int read(File file)
        {
            final byte[] data = Files.readAllbytes(file.toPath());
            return read(data);
        }
    
        int read(byte[] data);
    }
    
    class ReaderA
        implements Reader
    {
        int read(byte[] data)
        {
            // do somethingA
        }
    }
    
    class ReaderB
        implements Reader
    {
        int read(byte[] data)
        {
            // do somethingB
        }
    }
    

    Notice how read(File) now is the same for all implementing classes. Naturally, you have to change the way the methods are called from ReaderX.read() to new ReaderX().read().