泛型编译错误

4

test1和test2有什么区别? 为什么test1编译出错?

import java.util.ArrayList;
import java.util.Collection;

class MyType {

}

class MyClass<T> {
    private Collection<MyType> myTypes = new ArrayList<MyType>();
    private Collection<T> myTs = new ArrayList<T>();

    public Collection<MyType> getMyTypes() {
        return myTypes;
    }

    public Collection<T> getMyTs() {
        return myTs;
    }
}



public class TestSimple {

    public void test1() {    

        MyClass myClass = new MyClass();

        for (MyType myType : myClass.getMyTypes())  {

        }
    }

    public void test2() {            
        MyClass myClass = new MyClass();

        Collection<MyType> myTypes = myClass.getMyTypes();
        for (MyType myType : myTypes)  {

        }
    }

    public void test3() {
         MyClass<Long> myClass = new MyClass<Long>();

          for (Long myType : myClass.getMyTs())  {

          }        
     }

}

5
为什么你在test1和test2中使用未经参数化的MyClass - Jim Garrison
因为我对类型一无所知。Java允许这种用法。 - Alex
但是如果我有 "public Collection<MyType> getMyTypes()",我正在等待 MyType 的集合。 - Alex
你遇到了什么编译错误? - StriplingWarrior
@StriplingWarrior 错误:类型不兼容 需要的类型:MyType 找到的类型:Object - Alex
4个回答

4
如果在类上定义了通用约束,并且在不提供任何通用约束的情况下实例化该类(即完全省略<>),那么您刚刚踏入了原始类型的领域,这里一切都变了。
根据Java语言规范
引入泛型之后,只有为了兼容遗留代码才允许使用原始类型。强烈建议在Java编程语言中引入泛型后编写的代码不要使用原始类型。未来的Java编程语言版本可能会禁止使用原始类型。
根据Angelika Langer的出色Java Generics FAQ
一个原始类型的方法或构造函数具有它们在类型擦除之后所具有的签名。如果类型擦除更改了参数类型,则对原始类型的方法或构造函数调用会生成未经检查的警告。
因此,通过将MyClass构造成原始类型(即,作为MyClass而不是MyClass<?>),您已完全退出了泛型,getMyTypes()的返回类型现在是原始类型Collection,而不是Collection<MyType>。因此,您无法使用具有类型MyType的增强for语法,而必须改用Object
当您需要一个未知参数化类型的MyClass时,更好的解决方案当然是使用MyClass<?>(而不仅仅是MyClass)。

2

我已将您的问题缩小到一个较小的示例中,如下所示:

import java.util.*;

public class TestSimple {

    static class MyClass<T> {
        private Collection<String> myTypes = new ArrayList<String>();

        public Collection<String> getMyTypes() {
            return myTypes;
        }
    }

    public void test1() {    
        MyClass myClass = new MyClass();
        for (String myType : myClass.getMyTypes())  {
        }
    }
}

我怀疑除非你特别指定,否则所有类型信息都会被剥离。因此,我的建议是更改你的声明:

before: MyClass myClass = new MyClass();
after:  MyClass<?> myClass = new MyClass();

2

Java对原始类型要求过于严格。如果你使用了泛型类的原始类型,那么类中所有与泛型信息有关的内容都会被忽略掉,即使是与该类的类型参数无关的内容也不例外。

class A<T> implements List<String>

    Set<Integer> var;

如果您使用原始的A,那么它将被视为。
class A implements List

    Set var;

这种严厉的对待并不必要;他们可能认为原始类型并不值得太多资源,所以他们采取了简单的方法,毫无选择地从原始类型中抹去所有通用信息。

很有趣,我之前没有意识到原始类型的这一点。在示例中应该将“List”更改为“ArrayList”或其他非接口类型。此外,看起来以相同方式实现泛型接口的原始类型确实保留了该类型信息。我想知道背后的决策是什么区别。 - Paul Bellora
1
要使A成为原始类型,它首先必须是一个泛型类。如果class A extends B<X>,那么A不是原始类型,因为A甚至不是泛型的。 - irreputable

0
test2 中,由于您没有对 MyClass 进行参数化,getMyTypes() 实际上会返回 Collection<Object>,这与 Collection<MyType> 不兼容。

为什么方法 public Collection<MyType> getMyTypes() 需要参数化为 MyType?例如,如果您在 MyType 上删除 <T>,并删除 Collection<T> getMyTs()void test2()void test3(),它将编译通过。 - corsiKa
@glowcoder 是的,但我也有“Collection<T> myTs = new ArrayList<T>();”,它不能被删除。 - Alex
@Alex请看下面我的回答。即使没有那些,问题仍然存在。 - corsiKa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接