简体   繁体   English

Java泛型:有界类型参数中的多重继承<T extends A & I>

[英]Java Generics: Multiple Inheritance in Bounded Type Parameters <T extends A & I>

I am about to create a factory which creates objects of a certain type T which extends a certain class A and another interface I. However, T must not be known. 我即将创建一个工厂,它创建某种类型的对象T,它扩展了某个类A和另一个接口I.但是,T必须是未知的。 Here are the minimum declarations: 以下是最低声明:

public class A { }
public interface I { }

This is the factory method: 这是工厂方法:

public class F {
    public static <T extends A & I> T newThing() { /*...*/ }
}

This compiles all fine. 这编译所有罚款。

When I try to use the method the following works fine: 当我尝试使用该方法时,以下工作正常:

A $a = F.newThing();

...while this does not: ......虽然这不是:

I $i = F.newThing();

The compiler complains: 编译器抱怨:

Bound mismatch: The generic method newThing() of type F is not applicable for the arguments (). 绑定不匹配:类型F的泛型方法newThing()不适用于arguments()。 The inferred type I&A is not a valid substitute for the bounded parameter 推断的类型I和A不是有界参数的有效替代

I can't understand why. 我不明白为什么。 It is clearly stated that "newThing returns something of a certain type T which does extend the class A and implement the interface I". 明确指出“newThing返回某种类型的某些类型T,它确实扩展了类A并实现了接口I”。 When assigning to A everything works (since T extends A) but assigning to I does not (because of what? , clearly the thing returned is both an A and an I) 当分配给一个一切正常(因为T延伸的),但分配给我并没有(因为什么?,清楚地返回的东西既是 A I)

Also: When returning an object, say B of the type class B extends A implements I , I need to cast it to the return type T, although B matches the bounds: 另外:当返回一个对象时,比如B class B extends A implements I ,我需要将它转换为返回类型T,尽管B匹配边界:

<T extends A & I> T newThing() {
    return (T) new B();
}

However, the compiler does not throw any warnings like UncheckedCast or the like. 但是,编译器不会抛出任何警告,如UncheckedCast等。

Thus my question: 因此我的问题:

  • What is going wrong here? 这里出了什么问题?
  • Is there an easy away to achieve the desired behavior (ie assigning to a variable of static type A or I), like there is in solving the return-type-problem by casting, in the factory method? 是否容易实现所需的行为(即分配给静态类型A或I的变量),就像在工厂方法中通过强制转换解决返回类型问题一样?
  • Why does the assignment to A work, while to I does not? 为什么分配到A工作,而我不工作?

-- -

EDIT: Here the complete code snippet which totally works using Eclipse 3.7, project set up for JDK 6: 编辑:这里完整的代码片段完全使用Eclipse 3.7,项目设置为JDK 6:

public class F {
    public static class A { }
    public static interface I { }

    private static class B extends A implements I {  }

    public static <T extends A & I> T newThing() {
        return (T) new B();
}

    public static void main(String... _) {
        A $a = F.newThing();
        // I $i = F.newThing();
    }
}

EDIT: Here is a complete example with methods and invocation which does work at runtime : 编辑:这是一个完整的示例,其中的方法和调用在运行时可以正常工作

public class F {
    public static class A {
        int methodA() {
            return 7;
        }
    }
    public static interface I {
        int methodI();
    }

    private static class B extends A implements I {
        public int methodI() {
            return 12;
        }
    }

    public static <T extends A & I> T newThing() {
        return (T) new B();
    }

    public static void main(String... _) {
        A $a = F.newThing();
        // I $i = F.newThing();
        System.out.println($a.methodA());
    }
}

As for the second question: 至于第二个问题:

Consider this case: 考虑这种情况:

 class B extends A implements I {}
 class C extends A implements I {}

Now, the following uses type inference: 现在,以下使用类型推断:

<T extends A & I> T newThing() {
  return (T) new B();
}

So you could call this: 所以你可以这样称呼:

C c = F.newThing(); //T would be C here

You see that T could be anything that extends A and I you can't just return an instance of B . 你看到T可以是扩展A 任何东西I不能只返回B的实例。 In the case above the cast could be written as (C)new B() . 在上面的情况下,演员表可以写成(C)new B() This would clearly result in an exception and thus the compiler issues a warning: Unchecked cast from B to T - unless you're supressing those warnings. 这显然会导致异常,因此编译器会发出警告: Unchecked cast from B to T - 除非您正在抑制这些警告。

This doesn't do what you expect it to. 这不符合您的预期。 T extends A & I indicates that the caller can specify any type that extends A and I , and you'll return it. T extends A & I表示调用者可以指定任何扩展AI类型,并且您将返回它。

I think that one way to explain it is by replacing the type parameter with the actual type. 我认为解释它的一种方法是将type参数替换为实际类型。

The parameterized signature of the methods is: 方法的参数化签名是:

public static <T extends A & B> T newThing(){
   return ...;
}

The <T extends A & B> is what is called a type parameter. <T extends A & B>是所谓的类型参数。 The compiler would expect that this value is actually substituted with the actual type (called type argument) when you actually use it. 当您实际使用它时,编译器会期望该值实际上被实际类型(称为类型参数)替换。

In the case of your method the actual type is decided by means of type inference. 在您的方法的情况下,实际类型是通过类型推断决定的。 That is, <T extends A & B> should be replaced by a real existing type that extends A and implements B. 也就是说, <T extends A & B>应该被扩展为A并实现B的实际现有类型替换。

So, let's say that classes C and D both extends A and implements B, then if your signature were like this: 所以,假设类C和D都扩展了A并实现了B,那么如果你的签名是这样的:

public static <T extends A & B> T newThing(T obj){
   return obj;
}

Then, by type inference, your method would be evaluated as follows: 然后,通过类型推断,您的方法将按如下方式进行评估:

public static C newThing(C obj){
   return obj;
}

if you invoke with newThing(new C()) . 如果使用newThing(new C())调用。

And would be as follows 并将如下

public static D newThing(D obj){
   return obj;
}

if you invoke with newThing(new D()) . 如果你使用newThing(new D())调用。

This would compile just fine! 这会编译得很好!

However, since you are not actually providing any kind of type to validate type inference in your method declaration, then the compiler could never be sure what is the actual type (type argument) of your type parameter <T extends A & B> . 但是,由于您实际上并未在方法声明中提供任何类型的类型推断来验证类型推断,因此编译器永远无法确定类型参数<T extends A & B>的实际类型(类型参数)是什么。

You might expect that the actual type is C, but there may be thousands of different classes that satisfy that criteria. 您可能希望实际类型是C,但可能有数千个不同的类满足该条件。 Which of those should the compiler use as the actual type of your type argument? 编译器应该将哪一个用作类型参数的实际类型?

Let's say that C and D are two classes that extend A and implements B. Which of these two actual types should the compiler use as type argument for your method? 假设C和D是扩展A和实现B的两个类。编译器将这两个实际类型中的哪一个用作方法的类型参数?

You could have even declared a type argument for which there is not even an existing type that you can use, like saying something that extends Serializable and Closable and Comparable and Appendable. 你甚至可以声明一个类型参数,甚至没有你可以使用的现有类型,比如说一些扩展Serializable和Closable以及Comparable和Appendable的东西。

And perhaps there is not a class in the whole world that satisfies that. 也许整个世界都没有一个课程能够满足这一要求。

As such, you must understand that the type parameter here is just a requirement for the compiler to validate the actual type that you use, a placeholder for the actual type; 因此,您必须了解此处的类型参数只是编译器验证您使用的实际类型(实际类型的占位符)的要求; and this actual type must exist at the end and the compiler will use it to replace appearances of T. Therefore the actual type (type argument) must be inferable from the context. 并且这个实际类型必须存在于最后,并且编译器将使用它来替换T的外观。因此,实际类型(类型参数)必须可以从上下文中推断出来。

Since the compiler cannot tell with certainty which is the actual type that you mean, basically because there is no way to determine that by type inference in this case, then you are forced to cast your type, to ensure the compiler that you know what you are doing. 既然编译器无法确定你的意思是哪个实际类型,主要是因为在这种情况下没有办法通过类型推断来确定,那么你被迫转换你的类型,以确保编译器你知道你是什么是做。

As such, you could implement your method using type inference like this: 因此,您可以使用类型推断实现您的方法,如下所示:

   public static <T extends A & B> T newThing(Class<T> t) throws Exception{
    return t.newInstance();
}

This way, you would be actually telling the compiler what is the actual type argument to be used. 这样,您实际上会告诉编译器要使用的实际类型参数是什么。

Take into account that when the bytecodes are generated, the compiler must substitute T for a real type. 考虑到生成字节码时,编译器必须用T代替实数类型。 There is no way to write method in Java like this 没有办法像这样在Java中编写方法

public static A & B newThing(){ return ... }

Right? 对?

I hope I have explained myself! 我希望我已经解释过了! This is not simple to explain. 这不是一个简单的解释。

Simplest solution is create an abstract base class that extends and implements whatever class and interfaces you want and return that type. 最简单的解决方案是创建一个抽象基类,它扩展并实现您想要的任何类和接口并返回该类型。 It doesn't matter that you're constraining your return type to extend this base class as you were already constraining the return type to its superclass. 由于您已经将返回类型约束为其超类,因此限制返回类型以扩展此基类并不重要。

eg. 例如。

class C {}
interface I {}

abstract class BaseClass extends C implements I {}
// ^-- this line should never change. All it is telling us that we have created a
// class that combines the methods of C and I, and that concrete sub classes will
// implement the abstract methods of C and I    


class X extends BaseClass {}
class Y extends BaseClass {}

public class F {

    public static BaseClass newThing() {
        return new X();
    }


    public static void main(String[] args) {
        C c = F.newThing();
        I i = F.newThing();
    }
}

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

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