Java 如何使具有相同名称、签名和返回类型的方法的两个类的行为类似于它们将实现相同的接口

Java 如何使具有相同名称、签名和返回类型的方法的两个类的行为类似于它们将实现相同的接口,java,generics,interface,java-8,Java,Generics,Interface,Java 8,我将用一个例子来解释我的问题 我有两个类NumberGeneratorApple和NumberGeneratorOrange。它们都有具有相同签名和返回类型的方法public Integer getNumber()。问题是,尽管接口符合逻辑,但它们并没有实现相同的接口。我可以用这个方法创建接口并修改这些类来实现它,但我不想这样做。这就是原因。这些类是从xml自动生成的。在实际例子中,有几十个这样的类。我们必须不时地生成它们,它会覆盖旧的。我不想在每次生成后手动更改每个类来实现接口。同样,对于其他

我将用一个例子来解释我的问题

我有两个类
NumberGeneratorApple
NumberGeneratorOrange
。它们都有具有相同签名和返回类型的方法
public Integer getNumber()
。问题是,尽管接口符合逻辑,但它们并没有实现相同的接口。我可以用这个方法创建接口并修改这些类来实现它,但我不想这样做。这就是原因。这些类是从xml自动生成的。在实际例子中,有几十个这样的类。我们必须不时地生成它们,它会覆盖旧的。我不想在每次生成后手动更改每个类来实现接口。同样,对于其他在同一项目上工作的人,甚至是在一段时间后对我来说,这样做可能并不明显。
这是第一节课:

public class NumberGeneratorApple {
    public Integer getNumber(){
        return 9;
    }
}  
第二个:

public class NumberGeneratorOrange {
    public Integer getNumber(){
        return 19;
    }
}
虽然它们没有实现相同的接口,但我需要以通用的方式使用它们。我只想做这样的事情:

public class ClassThatOperates<T extends NumberGeneratorInterface> {
    T generator;

    public ClassThatOperates(T generator) {
        this.generator = generator;
    }

    public void doSomeOperation(){
        //Obviously it won't work for NumberGeneratorApple and NumberGeneratorOrange
        // because they do not implement NumberGeneratorInterface
        //And if I change "<T extends NumberGeneratorInterface>" to "<T>"
        // I cannot write method call "generator.getNumber()"
        System.out.print("The number with constant is: ");
        System.out.println(generator.getNumber() + 5);
    }
}
@Override
public Integer getNumber() {
    return super.getNumber();
}
正如您所看到的,这样做是不可能的,因为
NumberGeneratorApple
NumberGeneratorOrange
都没有实现
NumberGeneratorInterface
。然而,我想出了一些解决办法,但根据我的直觉,我认为这是相当糟糕的。我创建了包装器类:

public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
}

这有点棘手。一开始可能不太明显,但当您对其中一个类的对象调用getNumber()时,您实际上调用了如下内容:

public class ClassThatOperates<T extends NumberGeneratorInterface> {
    T generator;

    public ClassThatOperates(T generator) {
        this.generator = generator;
    }

    public void doSomeOperation(){
        //Obviously it won't work for NumberGeneratorApple and NumberGeneratorOrange
        // because they do not implement NumberGeneratorInterface
        //And if I change "<T extends NumberGeneratorInterface>" to "<T>"
        // I cannot write method call "generator.getNumber()"
        System.out.print("The number with constant is: ");
        System.out.println(generator.getNumber() + 5);
    }
}
@Override
public Integer getNumber() {
    return super.getNumber();
}
现在我可以这样称呼它:

public class Main {
    public static void main(String[] args) {
        ClassThatOperates<NumberGeneratorAppleWrapper> classThatOperatesApple = new ClassThatOperates<>(new NumberGeneratorAppleWrapper());
        ClassThatOperates<NumberGeneratorOrangeWrapper> classThatOperatesOrange = new ClassThatOperates<>(new NumberGeneratorOrangeWrapper());
        classThatOperatesApple.doSomeOperation();
        classThatOperatesOrange.doSomeOperation();
    }
}
这种方法的优点是,我们不必在每个生成的类中手动添加
implements NumberGeneratorInterface
,而是在每个生成后重复相同的工作(覆盖旧类)。我们只需要在生成某些新的、附加的类时添加一个新的包装器

我知道在这种情况下,我可以去掉运行的
类中的泛型,只声明
numbergenerator接口生成器没有
等等(我甚至应该这样做),因此代码会更简单,但我想让这个示例与我在某个实际项目中发现的非常相似。我写道:我有,我做,我提出等,但事实上它是基于已经存在的代码,我在一些项目中发现

以下是我的问题:
1.有更好的解决方案吗?
2.这种解决方案是所谓的“低级趣味”吗?
3.如果我不得不使用这种解决方案,可能我的整个方法都是错误的?
4.如果没有更好的解决方案,即使整个方法都是错误的,那么这段代码中还有什么可以改进的地方(包括去掉泛型)?

考虑:

class ClassThatOperates
{
    final Supplier<Integer> m_generator;

    ClassThatOperates(Supplier<Integer> generator)
    {
        m_generator = generator;
    }
    void doSomething()
    {
        ...
        m_generator.get();
    }
}

NumberGeneratorApple nga = new NumberGeneratorApple();
ClassThatOperates cta = new ClassThatOperates(nga::generate);
操作的类
{
最终供应商m_发电机;
运行的类(供应商生成器)
{
m_发电机=发电机;
}
无效剂量测定法()
{
...
m_generator.get();
}
}
NumberGeneratorApple nga=新的NumberGeneratorApple();
操作cta的类=操作的新类(nga::generate);
  • 有更好的解决办法吗
  • 我觉得你的解决方案不错。您已经使用了,它使用现有功能来符合其他不相关的接口。在不更改
    NumberGeneratorApple
    NumberGeneratorOrange
    的情况下,您已经将这些类的功能调整为
    NumberGeneratorInterface

    更一般地说,您可以让具有不同签名的现有方法适应一个接口,例如,如果您有

    public class NumberGeneratorApple {
        public Integer getAppleNumber(){
            return 9;
        }
    }
    
    然后,在实现接口时,适配器类将显式调用
    getAppleNumber

    public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
        @Override
        public Integer getNumber() {
            return getAppleNumber();
        }
    }
    
  • 这种解决方案是所谓的“低级趣味”吗
  • 这种溶液不会留下不好的味道。适配器模式是一种成熟的软件设计模式

  • 如果我不得不使用这样的解决方案,也许我的整个方法都是错误的
  • 如果您无法更改现有类,例如
    NumberGeneratorApple
    ,那么适配器模式就是最好的选择。如果您可以更改它们,那么只需让类直接实现必要的接口即可

    public class NumberGeneratorApple implements NumberGeneratorInterface {
        @Override
        public Integer getNumber() {
            return 9;
        }
    }
    
    或者,如果方法签名不同:

    public class NumberGeneratorApple implements NumberGeneratorInterface {
        public Integer getAppleNumber() {
            return 9;
        }
    
        @Override
        public Integer getNumber() {
            return getAppleNumber();
        }
    }
    
  • 如果没有更好的解决方案,即使整个方法都是错误的,那么代码中还有什么可以改进的地方(包括去掉泛型)
  • 如果像
    NumberGeneratorApple
    这样的类实际上只有一个方法,并且对于这个问题来说,它们不仅仅是使用多个方法简化更复杂的类,那么您可以使用方法引用,正如另一个答案所暗示的那样。方法引用可以键入为供应商,而不是声明自己的接口
    NumberGeneratorInterface


    使用java8,您可以实例化一个自定义类

    NumberGeneratorApple apple = new NumberGeneratorApple();
    
    并从中创建通用供应商:

    Supplier<Integer> numberGenerator = apple::getNumber;
    

    听起来怎么样?

    你也可以使用反射。然而,不确定这是否比你的方法更好。也许反射是一种方法,但我认为它可能相当复杂,结果比这种方法更糟糕,尤其是在代码的清洁度方面。Ad 4。如果类中只有一个方法,比如
    NumberGeneratorApple
    ,那么这个解决方案是有意义的。但正如您所注意到的,这个例子只是简化了更复杂的情况,在这种情况下,此类类有多个方法需要调整。因此,在我的实际情况中,这种解决方案无法使用。感谢您提到此供应商解决方案只能在特定场景中使用。您的解决方案很好,但仅限于此简化案例。在我的实际案例中,在
    NumberGeneratorApple
    NumberGeneratorApple
    中有许多类似的方法,使用这种解决方案可能没有意义。然而,这个答案是正确的,可能会帮助其他有类似p
    NumberGeneratorOrange ngo = new NumberGeneratorOrange();
    ClassThatOperates cto = new ClassThatOperates(ngo::getNumber);
    cto.doSomeOperation();
    
    NumberGeneratorApple apple = new NumberGeneratorApple();
    
    Supplier<Integer> numberGenerator = apple::getNumber;
    
    Integer number = numberGenerator.get();
    externalObject.execute(numberGenerator);