您的位置:首页 > 编程语言 > Java开发

java泛型程序设计——Varargs 警告+不能实例化类型变量

2015-12-08 11:20 796 查看

【0】README

0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java泛型程序设计 的 Varargs 警告+不能实例化类型变量 的知识;

【1】 Varargs 警告

1.1)一个相关问题: 向参数个数可变的方法传递一个泛型类型的实例;

1.1.1)考虑以下方法, 它的参数个数是可变的:

public static<T> void addAll(Collection<T) coll, T...ts)
{
......
}


1.1.2)应该记得, 实际上参数ts 是一个数组, 包含所有实参, 考虑以下调用:

Collection<Pair<String>> table = ......;
Pair<String> pair1 = ....;
Pair<String> pair2 = ....;
addAll(table, pair1, pair2);


1.1.3)为了调用上述方法: java 虚拟机必须建立一个 Pair 数组, 这就违反了前面的规则, 不过, 对于这种case , 你只会得到一个警告, 而不是 错误;

1.4)可以采用以下两种方法来抑制这种警告(Methods):(解决方法)

M1)一种方法是 为 包含 addAll 调用的方法增加 标注

@SuppressWarnings("unchecked");


M2)在java se 7中, 还可以用 @SafeVarargs 直接标注addAll 方法:

@SafeVarargs
public static <T> void addAll(Collection<T> coll, T...ts);


现在就可以用 泛型类型来调用这个方法了;

Annotation)

A1)可以使用 @SafeVarargs 标注来消除创建泛型数组的有关限制, 方法如下:

@SafeVarargs
static <E> E[] array(E... array)
{
return array;
}


A2)现在可以调用:

Pair<String>[] table = array(pair1, pair2);


这看起来方便, 不过隐藏着危险, 如下代码:

Object[] objarray = table;
objarray[0] = new Pair<Employee>();


能顺利运行而不会出现 ArrayStoreException 异常 (因为数组存储只会检查擦除的类型), 但在处理 table[0] 时 你会在别处得到一个异常;

【2】不能实例化类型变量

2.1)不能使用像 new T(…), new T[…] 或 T.class 这样的表达式中的类型变量。

2.1.1)看个荔枝:(下面的 Pair 构造器就是非法的)

public Pair()
{
first = new T();
second = new T(); // ERROR
}


2.1.2)类型擦除将T 改变成 Object, 而且, 本意肯定不希望调用 new Object()。

2.1.3)但是可以通过反射调用 Class.newInstance 方法来构造泛型对象:

然而,我们却不能调用: first = T.class.newInstance();//ERROR


2.1.4)因为, 表达式 T.class 是不合法的, 必须像下面这样设计 API 以便可以支配Class 对象:

public static<T> Pair<T> makePair(Class<T> cl)
{
try{ return new Pair<>(c1.newInstance(), c1.newInstance()) }
catch(Exception ex) {return null;}
}


2.1.5)以上方法可以按照下列方式调用:

Pair<String>p = Pair.makePair(String.class);


Attention) Class 类本身就是泛型, 如, String.class 是一个 class《String》 的实例(事实上, 它是唯一的实例)。 因此, makePair 方法能够推断出 pair 的类型;

2.1.6) 不能构造一个 泛型数组:

public  static <T extends Comparable> T[] minmax(T[] a) { T[] mm = new T[2]; ...} //ERROR


类型擦除会让这个方法永远构造 Object[2]数组;

2.2)如果数组仅仅是作为一个类的私有实例域, 就可以将这个数组声明为 Object[], 并且在获取元素时进行类型转换。

2.2.1)看个荔枝: 如 ArrayLIst 可以这样实现:

public class ArrayList<E>
{
private Object[] elements;
...
@SuppressWarnings("unchecked")
public E get(int n)
{
return (E) elements
; //获取元素时进行类型转换
}
pubic void set(int n, E e)
{
elements
=e;
}
}


2.2.2)实际的实现没有这么清晰:

public class ArrayList<E>
{
paivate E[] elements;
...
public ArrayList(){ elements = (E[])new Object[0]; }
}


2.2.3)这里, 强制类型转换 E[] 是一个假想, 而类型 擦除使其无法察觉;

2.3)由于 minmax方法 返回 T[] 数组, 使得这一技术无法施展, 如果掩盖这个类型会有运行时错误。假设实现代码:

public static <T extends Comparable> T[] minmax(T...a)
{
Object[] mm = new Object[2];
...
reutrn (T[]) mm;
}


2.4)调用String[] ss = minmax(“tom”, “dick”, “Harry”);

对上述代码的分析(Analysis):

A1)编译时不会有任何警告。 但Object[] 引用赋给 String[] 变量时, 将会发生 ClassCastException 异常;

A2)在这种case下, 利用反射,调用 Array.newIntance;

public static <T extemds Comparable> T[] minmax(T... a)
{
T[] mm = (T[])mm.newInstance(a.getClass().getComponentType(), 2);
}


A3)ArrayList 类的toArray方法就没有这么幸运了。 它需要生成一个 T[] 数组, 但没有成分类类型。 因此, 有下面两种不同的形式:

Object[] toArray();
T[] toArray(T[] result);


第二个方法接收一个数组参数。 如果数组足够大, 就是用这个数组。 否则, 用result 的成分类型构造一个足够大的 新数组;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息