java通用方法如何约束方法类型参数? [英] How do java generic methods constrain the method type arguments?

查看:190
本文介绍了java通用方法如何约束方法类型参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在阅读泛型方法,并且认为我理解泛型类型参数如何约束方法参数类型,但是当我用实际代码测试了一些想法时,我得到了意想不到的结果。这是一个简单的泛型方法,我不明白:

  private static< T> void foo(T [] t1,T [] t2){
t2 [0] = t1 [0];
}
...
String [] stringArray = new String [] {1,2,3};
Integer [] integerArray = new Integer [] {4,5,6};
foo(stringArray,integerArray);

我会认为这个泛型方法受到限制,因此两个数组必须是相同的类型T,但实际上上面的代码编译得很好,即使一个数组的类型是String而另一个数组的类型是Integer。当程序运行时,它会产生一个运行时异常(ArrayStoreException)。

解决方案

由于@Bozho已经证明了这个问题可以但是为了演示如何考虑这段代码:

  public class Main {


//这是因数组中的类型擦除而失败的原始版本
private static< T> void foo(T [] t1,T [] t2){

t2 [0] = t1 [0];
}

//与foo()方法相同,但是类型擦除显示
private static void foo2(Object [] t1,Object [] t2){

// Integer []不应该包含字符串
t2 [0] = t1 [0];


$ b public static void main(String [] args){

String [] stringArray = new String [] {1, 2,3};
Integer [] integerArray = new Integer [] {4,5,6};

foo2(stringArray,integerArray);
}

}

这一切都与事实上Java中的数组是协变的,而泛型则不是。 这里有一篇有趣的文章。一个快速的报价说明了这一切:


Java语言中的数组是
covariant - 这意味着如果
Integer扩展Number(它是
),那么不仅整数
也是一个数字,而Integer []是
也是一个数字[],并且你可以自由地使用
传递或分配一个Integer [],其中
Number []被调用。 (如果Number是一个超类型
Integer,则Number []是一个超类型
的Integer []。),您可能会认为
与generic相同类型以及
- 该List是List的超类型,并且您可以传递
列表,其中List是
预期。不幸的是,它并不是
的工作方式。



事实证明,
不会这样工作的一个很好的理由:它会打破
类型安全泛型被认为是
提供的。想象一下,您可以将一个
列表分配给一个列表。然后
下面的代码可以让你
把一些不是Integer
的东西放到List中:



 列表<整数> li = new ArrayList< Integer>(); 
列表<号码> ln = li; //非法
ln.add(new Float(3.1415));

因为ln是一个List,所以向它添加一个Float似乎是完全合法的。但是,如果ln与li混叠在一起,那么它将打破li的定义中隐含的类型安全承诺 - 它是一个整数列表,这就是为什么泛型不能协变的原因。

I've been reading about generic methods, and thought I understood how the generic type argument would constrain the method parameter types, but when I tested out some of the ideas with actual code, I got unexpected results. Here is a simple generic method that I don't understand:

private static <T> void foo(T[] t1, T[] t2){  
    t2[0] = t1[0];
}
...
String[] stringArray = new String[]{"1", "2", "3"};
Integer[] integerArray = new Integer[]{4,5,6};
foo(stringArray, integerArray);

I would have thought that this generic method was constrained so that the two arrays had to be of the same type T, but in practice the code above compiles just fine, even though one array is of type String and the other is of type Integer. When the program runs, it generates a run-time exception (ArrayStoreException).

解决方案

As @Bozho has demonstrated the problem can be fixed, however to demonstrate what is going on consider this code:

public class Main {


  // This is the original version that fails because of type erasure in arrays
  private static <T> void foo(T[] t1, T[] t2) {

    t2[0] = t1[0];
  }

  // The same method as foo() but with the type erasure demonstrated
  private static void foo2(Object[] t1, Object[] t2) {

    // Integer[] should not contain String 
    t2[0] = t1[0];
  }


  public static void main(String[] args) {

    String[] stringArray = new String[]{"1", "2", "3"};
    Integer[] integerArray = new Integer[]{4, 5, 6};

    foo2(stringArray, integerArray);
  }

}

This is all to do with the fact that arrays in Java are covariant, whereas generics are not. There is an interesting article about it here. A quick quote says it all:

Arrays in the Java language are covariant -- which means that if Integer extends Number (which it does), then not only is an Integer also a Number, but an Integer[] is also a Number[], and you are free to pass or assign an Integer[] where a Number[] is called for. (More formally, if Number is a supertype of Integer, then Number[] is a supertype of Integer[].) You might think the same is true of generic types as well -- that List is a supertype of List, and that you can pass a List where a List is expected. Unfortunately, it doesn't work that way.

It turns out there's a good reason it doesn't work that way: It would break the type safety generics were supposed to provide. Imagine you could assign a List to a List. Then the following code would allow you to put something that wasn't an Integer into a List:

List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));

Because ln is a List, adding a Float to it seems perfectly legal. But if ln were aliased with li, then it would break the type-safety promise implicit in the definition of li -- that it is a list of integers, which is why generic types cannot be covariant.

这篇关于java通用方法如何约束方法类型参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆