Java - 当返回类型对自己的方法参数类型使用泛型时覆盖扩展接口的返回类型

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5056595/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 09:12:42  来源:igfitidea点击:

Java - Overriding return type of extended interface when return type uses generics for own method parameter types

javagenericsmethodstypesreturn

提问by Omnaest

i've stumbled upon a curiosity in the java inheritance, and I wanted you to ask for better ideas on that:

我偶然发现了对 java 继承的好奇心,我希望你能就此提出更好的想法:

Assume two interfaces A and A1

假设有两个接口 A 和 A1

Interface A1 extends A

接口 A1 扩展 A

Interface A has a method which returns a generic type.

接口 A 有一个返回泛型类型的方法。

The generic type would be like GenericType<T>.

泛型类型类似于GenericType<T>.

A basic idea is now to change this generic return type from GenericType<Object>in Interface A into GenericType<String>in Interface A1

现在的一个基本想法是将这个通用返回类型从GenericType<Object>接口 A更改 为 GenericType<String>接口 A1

Well seems to be easy at first (bad things will come later on)

好吧,一开始似乎很容易(不好的事情以后会发生)

We declare Interface A like

我们声明接口 A 就像

public interface InterfaceA {
  public GenericType<? extends Object> getAGenericType();  
}

and Interface A1 like

和接口 A1 一样

public interface InterfaceA1 extends InterfaceA
{
  @Override
  public GenericType<String> getAGenericType();
}

As you see we are forced to write GenericType<? extends Object>in Interface A itself to allow overriding it with generic based "subclasses". (In fact the generic parameter of the generictype is subclassed not the generic type itself)

如您所见,我们被迫GenericType<? extends Object>在接口 A 本身中进行编写,以允许使用基于泛型的“子类”覆盖它。(实际上,generictype 的泛型参数是子类化的,而不是泛型类型本身)

Now assume the GenericType has its own method looking like:

现在假设 GenericType 有自己的方法,如下所示:

public interface GenericType<D>
{
  public void doSomethingWith( D something );
}

Now trying to instantiate A1 works great.

现在尝试实例化 A1 效果很好。

Rather trying to instantiate A will suck. To see why look at this "use the interface" class:

而试图实例化 A 会很糟糕。要了解为什么要查看这个“使用接口”类:

public class LookAtTheInstance
{
  @SuppressWarnings("null")
  public static void method()
  {
    InterfaceA a = null;
    InterfaceA1 a1 = null;

    GenericType<String> aGenericType = a1.getAGenericType();

    GenericType<? extends Object> aGenericType2 = a.getAGenericType();
    Object something = null;
    aGenericType2.doSomethingWith( something );
  }
}

You ask: "And now?"

你问:“现在呢?”

It does not work on the last lines. In fact the parameter "something" is not even from type "Object" it is from Type "? extends Object". So you cannot pass the declared "Object" type. You can't pass anything at all.

它在最后几行不起作用。事实上,参数“something”甚至不是来自“Object”类型,而是来自“? extends Object”类型。所以你不能传递声明的“对象”类型。你根本无法通过任何东西。

So you end up declaring nice interfaces which, as it turns out, cannot be instantiated right.

所以你最终声明了很好的接口,事实证明,这些接口不能正确实例化。

Do you have ideas how to model such a use case, where the subclasses will have to override the return type, while the return type is a generics?

您是否有想法如何为这样的用例建模,其中子类必须覆盖返回类型,而返回类型是泛型?

Or how would you go around such a model case?

或者你会如何解决这样的模型案例?

Or am I just missing a simple point in the generic declaration and my example is possible this way?

或者我只是在泛型声明中遗漏了一个简单的点,而我的示例可以通过这种方式实现?

----------- (1) edit due to answers -----------

----------- (1) 因答案而编辑-----------

A very good basic idea is making the interface A more abstract! I had exactly the same idea first, but... (this has to come)

一个非常好的基本思想是让界面 A 更加抽象!我首先有完全相同的想法,但是......(这个必须来)

Assume doing this:

假设这样做:

We introduce a new interface AGeneric

我们引入了一个新的接口 AGeneric

public interface InterfaceAGeneric<T>{
  public GenericType<T> getAGenericType();
}

Now we will have to extend A and A1 from this new interface:

现在我们必须从这个新接口扩展 A 和 A1:

public interface InterfaceA extends InterfaceAGeneric<Object>{}
public interface InterfaceA1 extends InterfaceAGeneric<String>{}

That works fine, althought it breaks the path of the original inheritance.

这很好用,虽然它打破了原始继承的路径。

If we want A1 still be extendable from A, we have to change A1 to

如果我们希望 A1 仍然可以从 A 扩展,我们必须将 A1 更改为

public interface InterfaceA1 extends InterfaceA, InterfaceAGeneric<String>{}

and there a problem is again. This does not work, since we extend indirectly the same interface with different generic types. This is unfortunately not allowed.

问题又来了。这不起作用,因为我们使用不同的泛型类型间接扩展了相同的接口。不幸的是,这是不允许的。

You see the problem?

你看到问题了吗?

-

——

And to point to another circumstance:

并指出另一种情况:

If you cast the GenericType<? extends Object>to GenericType<Object>it obviously works. Example:

如果您将其投射GenericType<? extends Object>GenericType<Object>它显然有效。例子:

public class LookAtTheInstance
{
  public static void main( String[] args )
  {
    InterfaceA a = new InterfaceA()
    {
      @Override
      public GenericType<? extends Object> getAGenericType()
      {
        return new GenericType<Object>()
        {
          @Override
          public void doSomethingWith( Object something )
          {
            System.out.println( something );
          }
        };
      }
    };
    ;

    @SuppressWarnings("unchecked")
    GenericType<Object> aGenericType2 = (GenericType<Object>) a.getAGenericType();

    Object something = "test";
    aGenericType2.doSomethingWith( something );
  }  
}

So it seems for me that the resolving of the parameter type of the method

所以在我看来,方法的参数类型的解析

public interface GenericType<D extends Object>
{
  public void doSomethingWith( D something );
}

is wrong.

是错的。

If D is unified with "? extends Object" why the parameter type is not forced to be "Object"?

如果D与“? extends Object”统一,为什么参数类型不强制为“Object”?

Wouldnt this make more sence?

这不是更有意义吗?

采纳答案by Christian Semrau

A basic idea is now to change this generic return type from GenericType in Interface A into GenericType in Interface A1

现在的一个基本想法是将此通用返回类型从接口 A 中的 GenericType 更改为接口 A1 中的 GenericType

This is not possible, because Java Generics are invariant. [1]

这是不可能的,因为Java 泛型是不变的。[1]

As you found out, you cannot have an interface declaring a method that returns GenericType<Object>and in a sub interface override the method to return GenericType<String>: The latter return type is not a subtype of the former. And for good reason!

正如您所发现的,您不能让接口声明返回的方法,GenericType<Object>并且在子接口中覆盖要返回的方法GenericType<String>:后者返回类型不是前者的子类型。并且有充分的理由!

You tried to

你试图

extend indirectly the same interface with different generic types. This is unfortunately not allowed.

使用不同的泛型类型间接扩展相同的接口。不幸的是,这是不允许的。

There is no way this could possibly work: E.g. what should be the type of Ein public E set(int index, E element)in a class that implemented both List<String>and List<Object>? Your subclassed interface would have to produce a similar hybrid: The return value of getAGenericTypein the sub interface would have to implement both the GenericType<String>and the GenericType<Object>interface. And as we saw, this is impossible.

这不可能起作用:例如,Epublic E set(int index, E element)同时实现List<String>和的类中, in的类型应该是什么List<Object>?您的子类接口必须产生类似的混合体:getAGenericType子接口中的返回值必须同时实现GenericType<String>GenericType<Object>接口。正如我们所见,这是不可能的。

The compiler does not know what you are going to do with the type parameter in GenericType(although it theoretically could find out, it doesn't). If you had a variable of type GenericType<String>and assigned a GenericType<Object>to it, you may very well end up putting a Longinstance where a Stringis expected, and get a ClassCastExceptionwhere you won't expect one.

编译器不知道您要对 in 中的类型参数做什么GenericType(虽然它理论上可以发现,但它没有)。如果您有一个类型的变量GenericType<String>GenericType<Object>为其分配了 a ,那么您很可能最终会Long在 aString预期的地方放置一个实例,并ClassCastException在您不希望有的地方得到 a 。

In the doSomethingWithmethod of your variable GenericType<? extends Object> aGenericType2you can pass one thing: null. nullis the only object reference that has a subtype of ? extends Object. The lower bound type of ? extends Objectis the null type, which cannot be expressed in Java, and only implicitly exists as the type of the nullreference.

doSomethingWith变量的方法中,您GenericType<? extends Object> aGenericType2可以传递一件事:null. null是唯一具有子类型的对象引用? extends Object的下界类型? extends Object是 null 类型,在 Java 中无法表达,只能作为null引用的类型隐式存在。

[1] http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29#Java

[1] http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29#Java

回答by Sergey Vedernikov

@Override annotation:

@Override 注释:

When overriding a method, you might want to use the @Override annotation that instructs the compiler that you intend to override a method in the superclass. If, for some reason, the compiler detects that the method does not exist in one of the superclasses, it will generate an error.

覆盖方法时,您可能希望使用 @Override 批注来指示编译器您打算覆盖超类中的方法。如果由于某种原因,编译器检测到该方法不存在于其中一个超类中,则会生成错误。

With this annotation you cannot change return type of function.

使用此注释,您无法更改函数的返回类型。

If you want to override return type, just make interface A more abstract, add generic to this interface:

如果要覆盖返回类型,只需使接口 A 更抽象,在此接口中添加泛型:

public interface InterfaceA<T> {
  public GenericType<T> getAGenericType();  
}

Sample about overriding a generic method in a generic class.

关于在泛型类中覆盖泛型方法的示例。

回答by Mihir

I dont know if this is what you are expecting but You can declare your interface something like this

我不知道这是否是您所期望的,但是您可以像这样声明您的界面

public interface Interface <K extends Object> { ... }    

While your class might look like
public class InterfaceImpl extends Interface<String> { ... }

虽然您的课程可能看起来像
public class InterfaceImpl extends Interface<String> { ... }

回答by SimonC

The trouble is that InterfaceAdoesn't know what type it's holding. If you get InterfaceA to take a generic argument then you could do this:

问题是InterfaceA不知道它持有什么类型。如果你让 InterfaceA 接受一个泛型参数,那么你可以这样做:

public interface InterfaceA<T>
{
  public GenericType<T> getAGenericType();  
}

public interface InterfaceA1 extends InterfaceA<String>
{
  @Override
  public GenericType<String> getAGenericType();
}

public class LookAtTheInstance
{
  @SuppressWarnings("null")
  public static void method()
  {
    InterfaceA<String> a = null;
    InterfaceA1 a1 = null;

    GenericType<String> aGenericType = a1.getAGenericType();

    GenericType<String> aGenericType2 = a.getAGenericType();
    String something = null;
    aGenericType2.doSomethingWith( something );
  }
}

回答by David

I'm several years late to the party, but I found this page while searching for a related question and none of the answers really hit on the central issue, which I think is worth clarifying. Let's look at a slightly-more-fleshed-out example:

我参加聚会晚了几年,但我在搜索相关问题时发现了这个页面,但没有一个答案真正触及核心问题,我认为这是值得澄清的。让我们看一个稍微充实一点的例子:

interface GenericType<D> {
    D getAValue();
    void doSomethingWith(D value);
}

class StringType implements GenericType<String> {
    @Override
    public String getAValue() {
        return "Hello World";
    }

    @Override
    public void doSomethingWith(final String value) {
        System.out.println(value.length());
    }
}


interface InterfaceA {
    GenericType<? extends Object> getAGenericType();
}

interface InterfaceA1 extends InterfaceA {
    @Override
    GenericType<String> getAGenericType();
}

class AnActualA1 implements InterfaceA1 {
    @Override
    public GenericType<String> getAGenericType() {
        return new StringType();
    }
}


class LookAtTheInstance {
    public static void method() {
        InterfaceA1 a1 = new AnActualA1();

        // 'g1' is a StringType, which implements GenericType<String>; yay!
        GenericType<String> g1 = a1.getAGenericType();

        // Everything here is fine.
        String value = g1.getAValue();
        g1.doSomethingWith("Hello World");


        // But if we upcast to InterfaceA???
        InterfaceA a = (InterfaceA) a1;

        // Note: a.getAGenericType() still returns a new StringType instance,
        // which is-a GenericType<? extends Object>.
        GenricType<? extends Object> g = a.getAGenericType();

        // StringType.getAValue() returns a String, which is-an Object; yay!
        Object object = g.getAValue();

        // StringType.doSomethingWith() method requires a String as the parameter,
        // so it is ILLEGAL for us to pass it anything that cannot be cast to a
        // String. Java (correctly) prevents you from doing so.

        g.doSomethingWith(new Object()); // Compiler error!
    }
}

Conceptually, GenericType is NOT a GenericType, since a GenericType can only doSomethingWith() Strings, while a GenericType needs to be able to doSomethingWith() any object. GenericType is a compromise which the compiler allows you to use as a "base class" for any GenericType where D is-an Object, but only allows you to use a reference of that type to call methods that are type-safe for any possible runtime value of '?' (such as getAValue(), whose return value can always be safely cast to an Object since D is-an Object regardless of runtime type).

从概念上讲,GenericType 不是 GenericType,因为 GenericType 只能 doSomethingWith() 字符串,而 GenericType 需要能够 doSomethingWith() 任何对象。GenericType 是一种折衷,编译器允许您将其用作任何 GenericType 的“基类”,其中 D 是一个对象,但只允许您使用该类型的引用来调用对于任何可能的运行时都是类型安全的方法的价值 '?' (例如 getAValue(),其返回值始终可以安全地转换为 Object,因为 D 是一个 Object,而不管运行时类型如何)。

It's hard to tell what (if anything) the original poster was actually trying to model with this code, and in particular how much of the generic-ness of GenericType was really needed, but perhaps the inheritance should have gone the other way around?

很难说原始海报实际上试图用此代码建模什么(如果有的话),特别是真正需要多少 GenericType 的通用性,但也许继承应该相反?

/**
 * I can do something with instances of one particular type and one particular
 * type only.
 */
interface GenericType<D> {
    void doSomethingWith(D value);
}

/**
 * I can do something with instances of any type: I am-a GenericType<String>
 * because I can totally do something with a String (or any other kind of
 * Object).
 */
interface NonGenericType extends GenericType<Object>, GenericType<String> {
    @Override
    void doSomethingWith(Object value);
}


interface StringHandlerFactory { // nee InterfaceA1
    GenericType<String> getAGenericType();
}

/**
 * I extend StringHandlerFactory by returning a NonGenericType (which is-a
 * GenericType<String>, satisfying the interface contract, but also so much
 * more).
 */
interface ObjectHandlerFactory extends StringHandlerFactory { // nee InterfaceA
    @Override
    NonGenericType getAGenericType();
}

The downside being that there's no good way to express to the java compiler that NonGenericType extends GenericType, even though conceptually it could in this case, since GenericType never uses D as a return value. You have to manually specify each GenericType that you want it to extend. :(

缺点是没有好的方法可以向 Java 编译器表达 NonGenericType 扩展 GenericType,即使在这种情况下概念上它可以,因为 GenericType 从不使用 D 作为返回值。您必须手动指定您希望它扩展的每个 GenericType。:(

回答by yegor256

So you end up declaring nice interfaces which, as it turns out, cannot be instantiated right.

所以你最终声明了很好的接口,事实证明,这些接口不能正确实例化。

I think that the purpose of InterfaceAis not to be instantiated at all, because one of its dependable classes are generic. That's what you meant declaring:

我认为 的目的InterfaceA根本不是要实例化,因为它的可靠类之一是通用的。这就是你的意思声明:

public GenericType<? extends Object> getAGenericType()