Java 结合原始类型和泛型方法

Java 结合原始类型和泛型方法,java,generics,generic-method,raw-types,Java,Generics,Generic Method,Raw Types,这里有一个问题,第一个代码清单编译得很好(JDK1.6 | JDK1.7): 我收到一个编译器错误,说需要字符串[],但找到了对象[] 这意味着我的编译器将泛型方法解释为返回Object[],尽管它的参数是String[] 我仔细检查了toArray(myArray)方法签名: <T> T[] toArray(T[] a); T[]toArray(T[]a); 因此,它是一种参数化方法,其类型参数与列表的类型参数(即)没有任何关系 我不知道在这里使用原始类型如何影响使用独立类型参

这里有一个问题,第一个代码清单编译得很好(JDK1.6 | JDK1.7):

我收到一个编译器错误,说需要
字符串[]
,但找到了
对象[]

这意味着我的编译器将泛型方法解释为返回
Object[]
,尽管它的参数是
String[]

我仔细检查了
toArray(myArray)
方法签名:

<T> T[] toArray(T[] a);
T[]toArray(T[]a);
因此,它是一种参数化方法,其类型参数
与列表的类型参数(即
)没有任何关系

我不知道在这里使用原始类型如何影响使用独立类型参数的参数化方法的计算

  • 有人知道为什么这段代码不能编译吗
  • 有人知道记录这种行为的任何参考资料吗

    • 当您不使用泛型时,编译器将其视为原始类型,因此每个泛型类型都成为
      对象
      ,因此您无法传递
      字符串[]
      ,因为它需要
      对象[]

      这就是交易- 如果你使用

      List l = new ArrayList<String>();
      
      listl=newarraylist();
      

      您使用的是原始类型,其所有实例成员都将替换为其擦除副本。具体来说,实例方法声明中出现的每个参数化类型都将替换为其原始对应类型。有关详细信息,请参见JLS 4.8。

      不能将任何类型参数传递到toArray()方法,因为ArrayList是一个非参数化列表,它只知道它包含对象,仅此而已
      a.toArray()
      将始终返回对象[]数组。同样,如果您想指定它包含特定的字符串类型,则必须将其强制转换为
      (String[])
      (以及其中的所有危险)。

      这并不完全是您所期望的,但是如果您以原始形式引用泛型类,您将失去以任何方式对实例成员使用泛型的能力。它也不限于泛型方法,请查看以下内容:

       public class MyContainer<T> {
      
           public List<String> strings() {
               return Arrays.asList("a", "b");
           }
       }
      
       MyContainer container = new MyContainer<Integer>();
       List<String> strings = container.strings(); //gives unchecked warning!
      
      公共类MyContainer{
      公共列表字符串(){
      返回数组。asList(“a”、“b”);
      }
      }
      MyContainer容器=新的MyContainer();
      List strings=container.strings()//发出未经检查的警告!
      
      这是JLS()的相关部分:

      未从其超类或超接口继承的原始类型C的构造函数(§8.8)、实例方法(§8.4,§9.4)或非静态字段(§8.3)M的类型是对应于在对应于C的泛型声明中删除其类型的原始类型


      这是我在规范中找到的最接近于描述这种观察到的行为的描述:

      构造函数的类型(§8.8),实例方法(§8.8,§9.4),或 非静态字段(§8.3)M为未继承自 它的超类或超接口是在 对应于C的泛型声明。的静态成员的类型 原始类型C与泛型声明中的类型相同 对应于C

      将实际类型参数传递给 未从原始类型继承的原始类型的非静态类型成员 超类或超接口

      基于以上以及观察到的行为,我认为可以肯定地说,所有泛型参数类型都已从原始类型中删除。当然,在非遗留代码中不鼓励使用原始类型本身:

      只允许使用原始类型作为兼容性的让步 遗留代码的定义。在后写的代码中使用原始类型 在Java编程语言中引入泛型是非常必要的 非常气馁未来版本的Java 编程语言将不允许使用原始类型


      有趣的是,这种行为可以“解决”。使用两个接口,一个基本非通用接口和一个通用接口。然后编译器知道基本非泛型接口的所有函数都不是泛型的,并将这样处理它们

      如果使用流和函数链接,这种行为非常烦人,因此我会像下面这样解决它

      通过接口继承解决问题

      public interface GenericInterface<X extends Y> extends BaseInterface
      {
          X getValue();
      }
      
      public interface BaseInterface
      {
          String getStringValue();
      }
      

      如果您使用
      (String[])a.toArray()怎么办因为您没有使用泛型,所以强制转换是必须的。@HovercraftFullOfEels谢谢您的建议。我知道我可以使用它,但我正在学习程序员认证,我只是在玩一些泛型代码时发现了这一点,我无法解释它。以前从未遇到过这样的事情。所以,我更多的是寻找一个解释为什么会发生这种情况。有趣的发现。奇怪的是,如果将泛型参数显式指定为Object(ArrayList),错误就会消失。这很奇怪,因为对象应该是省略类型的默认值,错误消失。此外,如果将
      Integer
      用作类型:
      ArrayList a=new ArrayList()。可能重复:。编译器如何判断我没有使用泛型,列表的类型参数与泛型方法toArray的类型参数无关?编译器知道您是将“a”声明为ArrayList还是ArrayList。它知道这一点。。。嗯,看看你写的…;-)再想一想,我认为说编译器进入1.4编译模式是相当不准确的。我理解您的意思是,它将引用视为原始类型(就像JDK1.4中不存在泛型时一样),但您的语句可以被解释为,代码被编译时,就像我将
      target=1.4
      传递给我的编译器一样,生成1.4兼容的字节码,我认为情况并非如此。不管怎么说,你可能是李
       public class MyContainer<T> {
      
           public List<String> strings() {
               return Arrays.asList("a", "b");
           }
       }
      
       MyContainer container = new MyContainer<Integer>();
       List<String> strings = container.strings(); //gives unchecked warning!
      
      public interface GenericInterface<X extends Y> extends BaseInterface
      {
          X getValue();
      }
      
      public interface BaseInterface
      {
          String getStringValue();
      }
      
      GenericInterface object = ...;
      String stringValue = object.getStringValue();