简体   繁体   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

I will explain my question on an example. 我将以一个例子解释我的问题。

I have two classes NumberGeneratorApple and NumberGeneratorOrange . 我有两个类NumberGeneratorAppleNumberGeneratorOrange Both of them have method with the same signature and return type public Integer getNumber() . 它们都具有相同签名的方法,并返回类型public Integer getNumber() The problem is they do not implement the same interface althought it would be logical. 问题是他们没有实现相同的界面,尽管它是合乎逻辑的。 I could make interface with this method and modify these classes to implement it but I don't want to do so. 我可以使用此方法创建接口并修改这些类来实现它,但我不想这样做。 Here is why. 这就是原因。 These classes are generated automatically from let's say xml. 这些类是自动生成的,例如xml。 In real example there are dozens of such classes. 在实际例子中,有许多这样的类。 We have to generate them from time to time and it overwrites the old ones. 我们必须不时地生成它们并覆盖旧的。 I don't want to change each class manually to implement interface after each generation. 我不想手动更改每个类以在每一代之后实现接口。 Also it might not be obvious that it should be done for some other person working on the same project and even for me after some time. 此外,对于在同一项目中工作的其他人,甚至在一段时间后对我来说,也可能不应该这样做。
Here is the first class: 这是第一堂课:

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

and the second one: 第二个:

public class NumberGeneratorOrange {
    public Integer getNumber(){
        return 19;
    }
}

Althought they don't implement the same interface I need to use them in a generic way. 虽然他们没有实现我需要以通用方式使用它们的相同接口。 I just want to do something like this: 我只是想做这样的事情:

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

This is the interface (I know public is not necessary there and it is there by default. I just want to emphasize it): 这是界面(我知道公共在那里没有必要,它默认存在。我只想强调它):

public interface NumberGeneratorInterface {
    public Integer getNumber();
}

As you can see it is impossible to do such thing because neither NumberGeneratorApple nor NumberGeneratorOrange implements NumberGeneratorInterface . 正如你所看到的,不可能做到这一点,因为NumberGeneratorAppleNumberGeneratorOrange都没有实现NumberGeneratorInterface However I came up with some solution but following my instinct I think it is rather poor. 然而我想出了一些解决方案,但是按照我的直觉,我觉得它很差。 I made wrapper classes: 我做了包装类:

public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
}

and

public class NumberGeneratorOrangeWrapper extends NumberGeneratorOrange implements NumberGeneratorInterface {
}

It is a little bit tricky. 这有点棘手。 It might be not obvious at first but when you call getNumber() on object of one of this classes you call in fact something like this: 一开始可能并不明显,但是当你在其中一个类的对象上调用getNumber()时,你实际上就是这样调用的:

@Override
public Integer getNumber() {
    return super.getNumber();
}

Now I can call it this way: 现在我可以这样称呼它:

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

And I get the following output: 我得到以下输出:

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

The advantage of such approach instead of adding manually implements NumberGeneratorInterface to each generated class is that We don't have to repeat the same work after each generation (which overrides old classes). 这种方法的优点而不是手动为每个生成的类implements NumberGeneratorInterface是我们不必在每一代之后重复相同的工作(它会覆盖旧类)。 We only have to add a new wrapper when generation results in some new, additional class. 我们只需要在生成一些新的附加类时添加一个新的包装器。

I know that in such scenario I can get rid of generics in ClassThatOperates and just declare NumberGeneratorInterface generator; 我知道在这种情况下我可以在ClassThatOperates删除泛型并只声明NumberGeneratorInterface generator; without <T extends...> and so on (and I even should) so the code would be simpler, but I want to make this example quite similar to what I found in some real project. 没有<T extends...>等等(我甚至应该)所以代码会更简单,但我想让这个例子与我在一些真实项目中发现的非常相似。 I wrote: I have, I made, I came up etc. but in fact it is based on the already existing code that I found in some project. 我写道:我有,我做了,我来了等等,但事实上它是基于我在某个项目中发现的现有代码。

There are my questions: 有我的问题:
1. Is there a better solution? 1.有更好的解决方案吗?
2. Is this solution a so called "bad taste"? 这个解决方案是否是一种所谓的“不良品味”?
3. If I have to use such solution maybe my whole approach is wrong? 如果我必须使用这样的解决方案,那么我的整个方法可能是错的?
4. If there is no better solution even if the whole approach is wrong what could be improved in this code (including getting rid of generics)? 4.如果没有更好的解决方案,即使整个方法都是错误的,可以在此代码中改进(包括删除泛型)?

  1. Is there a better solution? 有更好的解决方案吗?

Your solution looks good to me. 你的解决方案对我来说很好。 You have used the Adapter Pattern , which uses existing functionality to conform to an otherwise unrelated interface. 您已使用适配器模式 ,它使用现有功能来符合其他不相关的接口。 Without changing NumberGeneratorApple and NumberGeneratorOrange , you have adapted those classes' functionality to NumberGeneratorInterface . 在不更改NumberGeneratorAppleNumberGeneratorOrange ,您已将这些类的功能调整为NumberGeneratorInterface

More generally, you can have existing methods with different signatures be adapted to an interface, eg if you had 更一般地说,您可以将具有不同签名的现有方法适用于界面,例如,如果您有

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

Then your adapter class would explicitly call getAppleNumber when implementing the interface. 然后,您的适配器类将在实现接口时显式调用getAppleNumber

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

This solution leaves no bad taste. 这种解决方案没有不好的味道。 The Adapter Pattern is a well established software design pattern. 适配器模式是一种成熟的软件设计模式。

  1. If I have to use such solution maybe my whole approach is wrong? 如果我必须使用这样的解决方案,也许我的整个方法是错的?

If you cannot change existing classes such as NumberGeneratorApple , then the Adapter Pattern is the way to go. 如果您无法更改现有的类(如NumberGeneratorApple ,则可以使用适配器模式。 If you can change them, then just have the classes implement the necessary interface directly. 如果你可以改变它们,那么让这些类直接实现必要的接口。

public class NumberGeneratorApple implements NumberGeneratorInterface {
    @Override
    public Integer getNumber() {
        return 9;
    }
}

Or, if the method signature is different: 或者,如果方法签名不同:

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

    @Override
    public Integer getNumber() {
        return getAppleNumber();
    }
}
  1. If there is no better solution even if the whole approach is wrong what could be improved in this code (including getting rid of generics)? 如果没有更好的解决方案,即使整个方法都是错误的,可以在这段代码中改进(包括摆脱泛型)?

If your classes such as NumberGeneratorApple really have exactly one method, and are not just simplifications of more complex classes with multiple methods for the purposes of this question, then you can use method references as another answer has hinted. 如果您的类(如NumberGeneratorApple确实只有一个方法,并且不仅仅是为了解决此问题而使用多个方法简化更复杂的类,那么您可以使用方法引用作为另一个答案。 Instead of declaring your own interface NumberGeneratorInterface , a method reference can be typed as a Supplier<Integer> . 可以将方法引用键入为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);
    }
}

Then you can use it as: 然后你可以用它作为:

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

With java8 you can instantiate one of your custom classes 使用java8,您可以实例化一个自定义类

NumberGeneratorApple apple = new NumberGeneratorApple();

and create a generic supplier from it: 并从中创建一个通用供应商:

Supplier<Integer> numberGenerator = apple::getNumber;

then you can pass it around or do whatever you need with it: 然后你可以传递它或做任何你需要的东西:

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

How's that sound? 那声音怎么样?

Consider: 考虑:

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