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

Java泛型:有界类型参数中的多重继承<;T扩展A&;I>;,java,generics,types,bounded-wildcard,Java,Generics,Types,Bounded Wildcard,我将创建一个工厂,它创建特定类型T的对象,该对象扩展了某个类a和另一个接口I。但是,T必须是未知的。以下是最低限度的声明: public class A { } public interface I { } 这是工厂方法: public class F { public static <T extends A & I> T newThing() { /*...*/ } } …但这并不是: I $i = F.newThing(); 编译器抱怨: 绑定不匹配:F类型

我将创建一个工厂,它创建特定类型T的对象,该对象扩展了某个类a和另一个接口I。但是,T必须是未知的。以下是最低限度的声明:

public class A { }
public interface I { }
这是工厂方法:

public class F {
    public static <T extends A & I> T newThing() { /*...*/ }
}
…但这并不是:

I $i = F.newThing();
编译器抱怨:

绑定不匹配:F类型的泛型方法newThing()不适用于参数()。推断的类型I&A不是有界参数的有效替代品

我不明白为什么。明确指出,“newThing返回某种类型的T,它扩展了类a并实现了接口I”。当分配给A时,一切都起作用(因为T扩展了A),但分配给I却不起作用(因为什么?),显然返回的东西既是A又是I)

另外:当返回一个对象时,比如说类型为
的B类扩展了A实现I
,我需要将其强制转换为返回类型T,尽管B与边界匹配:

<T extends A & I> T newThing() {
    return (T) new B();
}
T newThing(){
返回(T)新的B();
}
但是,编译器不会抛出任何类似UncheckedCast之类的警告

因此,我的问题是:

  • 这里出了什么问题
  • 在工厂方法中,是否有一种容易实现所需行为(即分配给静态类型a或i的变量)的方法,如通过强制转换解决返回类型问题
  • 为什么分配给某项工作,而给我的不工作?
--

编辑:这里是使用Eclipse3.7(为JDK6设置的项目)完全工作的完整代码片段:

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();
    }
}
公共类F{
公共静态类A{}
公共静态接口I{}
私有静态类B扩展了一个实现I{}
公共静态T newThing(){
返回(T)新的B();
}
公共静态void main(字符串…){
A$A=F.newThing();
//I$I=F.newThing();
}
}
编辑:下面是一个完整的示例,其中包含在运行时有效的方法和调用:

公共类F{
公共静态A类{
int methodA(){
返回7;
}
}
公共静态接口I{
int methodI();
}
私有静态类B扩展了实现I的{
公共int方法i(){
返回12;
}
}
公共静态T newThing(){
返回(T)新的B();
}
公共静态void main(字符串…){
A$A=F.newThing();
//I$I=F.newThing();
System.out.println($a.methodA());
}
}

这并没有达到您期望的效果
T扩展A&I
表示调用方可以指定扩展
A
I
的任何类型,您将返回它。

至于第二个问题:

考虑这种情况:

 class B extends A implements I {}
 class C extends A implements I {}
现在,下面使用类型推断:

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

您可以看到,
T
可以是扩展
A
I
的任何内容,您不能只返回
B
的实例。在上述情况下,cast可以写为
(C)new B()
。这显然会导致异常,因此编译器会发出警告:
未选中从B到T的强制转换
——除非您要消除这些警告。

最简单的解决方案是创建一个抽象基类,扩展和实现您想要的任何类和接口,并返回该类型。约束返回类型以扩展这个基类并不重要,因为您已经将返回类型约束到它的超类

例如


我认为解释它的一种方法是用实际类型替换类型参数

这些方法的参数化签名为:

public static <T extends A & B> T newThing(){
   return ...;
}
如果使用
newThing(newc())
调用

具体如下

public static D newThing(D obj){
   return obj;
}
如果使用
newThing(new D())
调用

这会很好

但是,由于您实际上没有提供任何类型来验证方法声明中的类型推断,因此编译器永远无法确定类型参数的实际类型(类型参数)

您可能期望实际类型是C,但可能有数千个不同的类满足该条件。编译器应该使用哪一个作为类型参数的实际类型

假设C和D是扩展A和实现B的两个类。编译器应该使用这两个实际类型中的哪一个作为方法的类型参数

您甚至可以声明一个类型参数,但它甚至没有可以使用的现有类型,比如说扩展了Serializable和Closable以及Comparable和Appendable

也许全世界没有一个阶级能满足这一点

因此,您必须理解这里的type参数只是编译器验证您使用的实际类型的一个要求,即实际类型的占位符;这个实际类型必须存在于最后,编译器将使用它来替换T的外观。因此,实际类型(类型参数)必须可以从上下文中推断出来

由于编译器无法确定您所指的实际类型,基本上是因为在这种情况下无法通过类型推断来确定,因此您必须强制转换类型,以确保编译器知道您在做什么

因此,您可以使用如下类型推断来实现您的方法:

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


我希望我已经解释清楚了!这并不容易解释。

不要在类或变量名中使用$。这是一个合法的字符,但该语言非正式地保留了它,以防止名称冲突。我认为您的示例代码中有一个错误:
a$a=F.newThing()不起作用。你确定你没有声明class A实现I吗{
public static <T extends A & B> T newThing(){
   return ...;
}
public static <T extends A & B> T newThing(T obj){
   return obj;
}
public static C newThing(C obj){
   return obj;
}
public static D newThing(D obj){
   return obj;
}
   public static <T extends A & B> T newThing(Class<T> t) throws Exception{
    return t.newInstance();
}
public static A & B newThing(){ return ... }