简体   繁体   English

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

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

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现在的一个基本想法是将此通用返回类型从接口 A 中的GenericType<Object>更改为接口 A1 中的GenericType<String>

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>如您所见,我们被迫编写GenericType<? extends Object> GenericType<? extends Object> in Interface A itself to allow overriding it with generic based "subclasses".在接口 A 本身中GenericType<? extends Object>以允许使用基于泛型的“子类”覆盖它。 (In fact the generic parameter of the generictype is subclassed not the generic type itself) (实际上,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.而试图实例化 A 会很糟糕。 To see why look at this "use the interface" class:要了解为什么要查看这个“使用接口”类:

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".事实上,参数“something”甚至不是来自“Object”类型,而是来自“? extends Object”类型。 So you cannot pass the declared "Object" type.所以你不能传递声明的“对象”类型。 You can't pass anything at all.你根本无法通过任何东西。

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!一个非常好的基本思想是让界面 A 更加抽象! I had exactly the same idea first, but... (this has to come)我首先有完全相同的想法,但是......(这个必须来)

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>如果您将GenericType<? extends Object> GenericType<? extends Object> to GenericType<Object> it obviously works. GenericType<? extends Object>GenericType<Object>它显然有效。 Example:例子:

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?这不是更有意义吗?

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 .这是不可能的,因为Java 泛型是不变的 [1] [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.正如您所发现的,您不能让接口声明返回GenericType<Object>的方法,并且在子接口中覆盖该方法以返回GenericType<String> :后者返回类型不是前者的子类型。 And for good reason!并且有充分的理由!

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: Eg what should be the type of E in public E set(int index, E element) in a class that implemented both List<String> and List<Object> ?没有办法,这可能可能工作:比如应如何类型Epublic E set(int index, E element)在实现这两个类List<String>List<Object> Your subclassed interface would have to produce a similar hybrid: The return value of getAGenericType in the sub interface would have to implement both the GenericType<String> and the GenericType<Object> interface.您的子类接口必须产生类似的混合:子接口中getAGenericType的返回值必须同时实现GenericType<String>GenericType<Object>接口。 And as we saw, this is impossible.正如我们所见,这是不可能的。

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).编译器不知道您将如何处理GenericType的类型参数(尽管它理论上可以发现,但它没有)。 If you had a variable of type GenericType<String> and assigned a GenericType<Object> to it, you may very well end up putting a Long instance where a String is expected, and get a ClassCastException where you won't expect one.如果您有一个GenericType<String>类型的变量并为其分配了一个GenericType<Object> ,您很可能最终会在需要String地方放置一个Long实例,并在您不希望有的地方得到ClassCastException

In the doSomethingWith method of your variable GenericType<? extends Object> aGenericType2doSomethingWith您的变量的方法GenericType<? extends Object> aGenericType2 GenericType<? extends Object> aGenericType2 you can pass one thing: null . GenericType<? extends Object> aGenericType2你可以传递一件事: null null is the only object reference that has a subtype of ? extends Object null是唯一具有子类型的对象引用? extends Object ? extends Object . ? extends Object The lower bound type of ? extends Object的下界类型? extends Object ? extends Object is the null type , which cannot be expressed in Java, and only implicitly exists as the type of the null reference. ? 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

I don't know if this is what you are expecting, but you can declare your interface something like:我不知道这是否是您所期望的,但您可以声明您的接口,例如:

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

While your class might look like:虽然您的课程可能看起来像:

public class InterfaceImpl implements Interface<String> { ... }

@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.覆盖方法时,您可能希望使用 @Override 批注来指示编译器您打算覆盖超类中的方法。 If, for some reason, the compiler detects that the method does not exist in one of the superclasses, it will generate an error.如果由于某种原因,编译器检测到该方法不存在于其中一个超类中,则会生成错误。

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. 关于在泛型类中覆盖泛型方法的示例。

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 不是 GenericType,因为 GenericType 只能 doSomethingWith() 字符串,而 GenericType 需要能够 doSomethingWith() 任何对象。 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 '?' GenericType 是一种折衷,编译器允许您将其用作任何 GenericType 的“基类”,其中 D 是一个对象,但只允许您使用该类型的引用来调用对于任何可能的运行时都是类型安全的方法的价值 '?' (such as getAValue(), whose return value can always be safely cast to an Object since D is-an Object regardless of runtime type). (例如 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.缺点是没有好的方法可以向 Java 编译器表达 NonGenericType 扩展 GenericType,即使在这种情况下概念上它可以,因为 GenericType 从不使用 D 作为返回值。 You have to manually specify each GenericType that you want it to extend.您必须手动指定您希望它扩展的每个 GenericType。 :( :(

The trouble is that InterfaceA doesn't know what type it's holding.问题是InterfaceA不知道它持有什么类型。 If you get InterfaceA to take a generic argument then you could do this:如果你让 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 );
  }
}

So you end up declaring nice interfaces which, as it turns out, cannot be instantiated right.所以你最终声明了很好的接口,事实证明,这些接口不能正确实例化。

I think that the purpose of InterfaceA is not to be instantiated at all, because one of its dependable classes are generic.我认为InterfaceA的目的根本不是要实例化,因为它的一个可靠类是通用的。 That's what you meant declaring:这就是你的意思声明:

public GenericType<? extends Object> getAGenericType()

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

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