【问题标题】:Duplicate code in static methods静态方法中的重复代码
【发布时间】:2018-01-04 09:26:02
【问题描述】:

我有两个用 JAVA 实现的阅读器。见下文:

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
}

问题。避免重复代码的最佳方法是什么?

我尝试在新的抽象类Reader 中提取重复代码,并尝试将read(final byte[] data) 抽象化并在子类ReaderAReaderB 中实现它。它不会起作用,因为方法是静态的。

【问题讨论】:

  • 如何将其抽象化?
  • 你为什么要把read(final byte[] date)方法抽象化?我认为你想提取重复的代码,所以这个方法不能是抽象的,因为你有(常见的)实现!
  • 删除静态,但我不会这样做,因为调用方法read(File)的整个代码会有很多变化。
  • read(final byte[] data) 在两个类中都不相同@NiklasP
  • 为什么这些方法需要是静态的?

标签: java code-duplication


【解决方案1】:

除非您从 read(byte[]) 中删除 static 修饰符并创建实例,否则您将无法使用继承来帮助您。

static 方法的行为不像实例方法,并且不能被覆盖。相反,超类和子类都将具有需要使用类名限定的单独方法。拉起read(File) 意味着它总是调用超类的read(byte[])。您仍然必须将read(File) 复制到每个子类,以使其使用该类自己的read(byte[])Ward's answer 中的代码也显示了这一点。

作为参考,请阅读此问题及其答案:Are static methods inherited in Java?

为了说明:您拥有的两个read(File) 方法与您在代码sn-p 中所说的“完全相同”。它们不都调用this.read(data),而是分别调用ReaderA.read(data)ReaderB.read(data)。看看read(byte[]) 调用是如何对两个完全不同的、不可覆盖的方法进行的。


如果它在你的能力范围内,我建议以一种不那么静态的方式重写读者:

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
    }
}

注意read(File) 现在对于所有实现类都是一样的。当然,您必须将调用方法的方式从 ReaderX.read() 更改为 new ReaderX().read()

【讨论】:

  • 它们的继承方式与 AFAIK 相同,至少字节码与虚拟方法没有任何不同。对我来说,这听起来像 JVM 将能够允许覆盖它们,但编译器不允许
  • 感谢您的洞察力,尤金。我把那句话改写了一下。
  • np,这已经困扰了我一段时间(现在仍然如此)。顺便说一句,很好的编辑!
【解决方案2】:

我认为您需要先问自己几个问题:

  1. 我真的需要两个静态方法吗?
  2. “方法”或类共有哪些代码?
  3. 是否会创建一个抽象类来编写此类实现并让 ReaderA 和 ReaderB 扩展它,减少重复代码?
  4. 是否应该有一个父类,然后创建从上面继承的类?

我认为您应该阅读 SOLID 原则,特别是 Open/Closed Principle 和 Dependency Inversion

【讨论】:

    【解决方案3】:

    如果您使用的是 Java SE 8,则可以将静态方法放在接口中。 Java接口静态方法类似于默认方法,只是我们不能在实现类中覆盖它们。这个特性可以帮助我们避免在实现类中实现不理想的结果。

    【讨论】:

      【解决方案4】:

      有一些可能性:

      1. 从其中一个类中删除方法并使用继承(您不能为静态方法调用 super.myMethod(),但除非您覆盖它,否则它会起作用)

      我建议不要这样做:您可能会在子类中获得其他可用的方法,而您可能不想这样做。

      1. 将其提取到两个类的公共超类

      2. 从一个类调用另一个类的方法。如果它们都保持相同的功能,这将起作用

      【讨论】:

        【解决方案5】:

        我不确定用静态方法保持这个实现是最好的,但如果是这样,你可以添加一个Function 参数。

        public class Reader {
        
            public static int read(final File file, Function<byte[], Integer> reader) {
                final byte[] data = Files.readAllbytes(file.toPath());
                return reader.apply(data);
            }
        
        }
        

        然后像这样使用它:

        public final class ReaderA {
            public static int read(final File file) {
                return Reader.read(file, ReaderA::read);
            }
            public static int read(final byte[] data) {
                // do somethingA
            }
        }
        

        自从在 Java 8 中引入functional interfacesmethod references 以来,几乎没有无法避免的重复代码部分。

        【讨论】:

          猜你喜欢
          • 2015-05-17
          • 1970-01-01
          • 1970-01-01
          • 2012-10-30
          • 1970-01-01
          • 1970-01-01
          • 2011-06-02
          • 1970-01-01
          • 2011-12-11
          相关资源
          最近更新 更多