Java 泛型:有界类型参数中的多重继承 <T extends A & I>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9824831/
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
Java Generics: Multiple Inheritance in Bounded Type Parameters <T extends A & I>
提问by scravy
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. Here are the minimum declarations:
我将要创建一个工厂,它创建某种类型 T 的对象,该对象扩展某个类 A 和另一个接口 I。但是,T 一定是未知的。以下是最低声明:
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 (). The inferred type I&A is not a valid substitute for the bounded parameter
绑定不匹配:F 类型的泛型方法 newThing() 不适用于参数 ()。推断的类型 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". When assigning to A everything works (since T extends A) but assigning to I does not (because of what?, clearly the thing returned isboth an A andan I)
我不明白为什么。明确指出“newThing 返回某种类型 T 的东西,它确实扩展了类 A 并实现了接口 I”。当分配给一个一切正常(因为T延伸的),但分配给我做(因为不是什么?,清楚地返回的东西是既是一个和一个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 (i.e. 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?
- Why does the assignment to A work, while to I does not?
- 这里出了什么问题?
- 是否可以轻松实现所需的行为(即分配给静态类型 A 或 I 的变量),就像在工厂方法中通过强制转换解决返回类型问题一样?
- 为什么对 A 的分配有效,而对 I 无效?
--
——
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());
}
}
采纳答案by Louis Wasserman
This doesn't do what you expect it to. T extends A & I
indicates that the callercan specify any type that extends A
and I
, and you'll return it.
这不会像你期望的那样做。 T extends A & I
表示调用者可以指定扩展A
和的任何类型,I
您将返回它。
回答by Thomas
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 anythingthat extends A
and I
you can't just return an instance of B
. In the case above the cast could be written as (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.
你看这T
可能是任何扩展A
和I
你不能只是返回的一个实例B
。在上面的情况下,演员表可以写成(C)new B()
. 这显然会导致异常,因此编译器会发出警告:Unchecked cast from B to T
- 除非您抑制这些警告。
回答by Dunes
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();
}
}
回答by Edwin Dalorzo
I think that one way to explain it is by replacing the type parameter with the actual 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. The compiler would expect that this value is actually substituted with the actual type (called type argument) when you actually use it.
这<T extends A & B>
就是所谓的类型参数。当您实际使用该值时,编译器会期望该值实际上被实际类型(称为类型参数)替换。
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. Which of those should the compiler use as the actual type of your type argument?
您可能期望实际类型是 C,但可能有数千个不同的类满足该条件。编译器应该使用哪些作为类型参数的实际类型?
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. There is no way to write method in Java like this
考虑到在生成字节码时,编译器必须用 T 替换真实类型。没有办法像这样在 Java 中编写方法
public static A & B newThing(){ return ... }
Right?
对?
I hope I have explained myself! This is not simple to explain.
我希望我已经解释了自己!这并不容易解释。