繁体   English   中英

如何创建两个具有相同名称,签名和返回类型的方法的类,就像它们实现相同的接口一样

[英]How to make two classes which both have method with the same name, signature and return type behave like they would implement the same interface

我将以一个例子解释我的问题。

我有两个类NumberGeneratorAppleNumberGeneratorOrange 它们都具有相同签名的方法,并返回类型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);
    }
}

这是界面(我知道公共在那里没有必要,它默认存在。我只想强调它):

public interface NumberGeneratorInterface {
    public Integer getNumber();
}

正如你所看到的,不可能做到这一点,因为NumberGeneratorAppleNumberGeneratorOrange都没有实现NumberGeneratorInterface 然而我想出了一些解决方案,但是按照我的直觉,我觉得它很差。 我做了包装类:

public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
}

public class NumberGeneratorOrangeWrapper extends NumberGeneratorOrange implements NumberGeneratorInterface {
}

这有点棘手。 一开始可能并不明显,但是当你在其中一个类的对象上调用getNumber()时,你实际上就是这样调用的:

@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();
    }
}

我得到以下输出:

The number with constant is: 14
The number with constant is: 24

这种方法的优点而不是手动为每个生成的类implements NumberGeneratorInterface是我们不必在每一代之后重复相同的工作(它会覆盖旧类)。 我们只需要在生成一些新的附加类时添加一个新的包装器。

我知道在这种情况下我可以在ClassThatOperates删除泛型并只声明NumberGeneratorInterface generator; 没有<T extends...>等等(我甚至应该)所以代码会更简单,但我想让这个例子与我在一些真实项目中发现的非常相似。 我写道:我有,我做了,我来了等等,但事实上它是基于我在某个项目中发现的现有代码。

有我的问题:
1.有更好的解决方案吗?
这个解决方案是否是一种所谓的“不良品味”?
如果我必须使用这样的解决方案,那么我的整个方法可能是错的?
4.如果没有更好的解决方案,即使整个方法都是错误的,可以在此代码中改进(包括删除泛型)?

  1. 有更好的解决方案吗?

你的解决方案对我来说很好。 您已使用适配器模式 ,它使用现有功能来符合其他不相关的接口。 在不更改NumberGeneratorAppleNumberGeneratorOrange ,您已将这些类的功能调整为NumberGeneratorInterface

更一般地说,您可以将具有不同签名的现有方法适用于界面,例如,如果您有

public class NumberGeneratorApple {
    public Integer getAppleNumber(){
        return 9;
    }
}

然后,您的适配器类将在实现接口时显式调用getAppleNumber

public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
    @Override
    public Integer getNumber() {
        return getAppleNumber();
    }
}
  1. 这个解决方案是所谓的“坏味道”吗?

这种解决方案没有不好的味道。 适配器模式是一种成熟的软件设计模式。

  1. 如果我必须使用这样的解决方案,也许我的整个方法是错的?

如果您无法更改现有的类(如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();
    }
}
  1. 如果没有更好的解决方案,即使整个方法都是错误的,可以在这段代码中改进(包括摆脱泛型)?

如果您的类(如NumberGeneratorApple确实只有一个方法,并且不仅仅是为了解决此问题而使用多个方法简化更复杂的类,那么您可以使用方法引用作为另一个答案。 可以将方法引用键入为Supplier<Integer> ,而不是声明自己的接口NumberGeneratorInterface

public class ClassThatOperates {
    Supplier<Integer> generator;

    public ClassThatOperates(Supplier<Integer> generator) {
        this.generator = generator;
    }

    public void doSomeOperation(){
        System.out.print("The number with constant is: ");
        System.out.println(generator.get() + 5);
    }
}

然后你可以用它作为:

NumberGeneratorOrange ngo = new NumberGeneratorOrange();
ClassThatOperates cto = new ClassThatOperates(ngo::getNumber);
cto.doSomeOperation();

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

NumberGeneratorApple apple = new NumberGeneratorApple();

并从中创建一个通用供应商:

Supplier<Integer> numberGenerator = apple::getNumber;

然后你可以传递它或做任何你需要的东西:

Integer number = numberGenerator.get();
externalObject.execute(numberGenerator);

那声音怎么样?

考虑:

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);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM