`
supportopensource
  • 浏览: 516144 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

泛型(core java 笔记)

阅读更多
1.为什么引入泛型
package generic;

import java.util.ArrayList;

public class ArrayListTest{
	public static void main(String[] args){
		ArrayList al = new ArrayList();
		al.add(1);
		System.out.println(al);
		Integer i1 = (Integer)al.get(0);
		System.out.println(i1);
		
		al.add("hello");
		/*
		 * 下面的代码编译正确,运行时报错
		 * Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
		 * */
		Integer i2 = (Integer)al.get(1);
		System.out.println(i2);
	}
}

结果:
[1]
1
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
	at generic.ArrayListTest.main(ArrayListTest.java:18)

以上的测试,可以看出Java SE 5.0之前,对ArrayList的使用存在两个问题:
(1)当获取一个值(使用get()方法)时,必须进行强制类型转换
(2)没有错误检查,可以添加任何对象。

泛型提供了一个更好的解决方案:类型参数(type parameters)
ArrayList类有一个类型参数用来指示元素的类型
ArrayList<String> al = new ArrayList<String>();
当调用get方法时不需要强制类型转换;使用add方法时,编译器将检查元素的类型。
package generic;

import java.util.ArrayList;

public class ArrayListTest2{
	public static void main(String[] args){
		ArrayList<Integer> al = new ArrayList<Integer>();
		al.add(1);
		System.out.println(al);
		Integer i1 = (Integer)al.get(0);
		System.out.println(i1);//(1)
		
		/* 下面的代码编译时报错
		 * The method add(Integer) in the type ArrayList<Integer> is not applicable for the arguments (String)
		 * */
		//al.add("hello");
		//Integer i2 = (Integer)al.get(1);
		//System.out.println(i2);
	}
}

结果:
[1]
1



2.简单泛型类的定义
一个泛型类(generic class)就是具有一个或多个类型变量(type variables)的类。
例如:Pair<T>泛型类型
package generic;

public class Pair<T>{
	
	private T first;
	private T second;
	
	public Pair(){
		this.first = null;
		this.second = null;
	}
	
	public Pair(T first, T second){
		this.first = first;
		this.second = second;
	}
	
	public void setFirst(T newValue){
		this.first = newValue;
	}
	
	public T getFirst(){
		return this.first;
	}
	
	public void setSecond(T newValue){
		this.second = newValue;
	}
	
	public T getSecond(){
		return this.second;
	}
	
}


Pair类引入了一个类型变量T,用尖括号(<>)括起来,并放在类名后面。
泛型类可以有多个类型变量
public class Pair<T, U>{...}

类定义中的类型变量可指定方法的返回值以及域和局部变量的类型:
public T getFirst(){}
praivate T first;
public void setFirst(T newValue){}

注释:类型变量使用大写形式,且比较短,这是很常见的。在Java库中,使用变量E表示集合的元素类型,K和V表示表的关键字与值的类型。T(需要时还可以用临时的字母U和S)表示“任何类型”。

具体的类型替换类型变量就可以实例化泛型类型,例如:
Pair<String>
可以将结果(Pair<String>)想象成带有构造器的普通类:
Pair<String>()
Pair<String>(String, String)
和方法:
String getFirst()
String getSecond()
void setFirst(String)
void setSecond(String)

使用Pair类:
计算静态的minmax方法遍历数组,并计算出最小值和最大值,用一个Pair对象返回这两个结果。
package generic;

public class ArrayAlg{
	
	public static Pair<String> minmax(String[] a){
		
		if(a == null || a.length == 0)
			return null;
		
		String min = a[0];
		String max = a[0];
		
		for(int i = 1; i < a.length; i++){
			if(min.compareTo(a[i]) > 0){
				min = a[i];
			}
			if(max.compareTo(a[i]) < 0){
				max = a[i];
			}
		}
		
		return new Pair<String>(min, max);
	}
	
	//test
	public static void main(String[] args){
		String[] words = {"Hello", "World", "very", "good"};
		Pair<String> mm = ArrayAlg.minmax(words);
		System.out.println("min = " + mm.getFirst());
		System.out.println("max = " + mm.getSecond());
	}
}

结果:
min = Hello
max = very



3.泛型方法
前面已经介绍了如何定义一个泛型类,下面介绍定义一个带有类型参数的方法:
public class ArrayAlg{
    public static <T> T getMiddle(T[] a){
		return a[a.length / 2];
	}
}

这是一个泛型方法。
泛型方法的格式:
修饰符 <类型变量1, 类型变量2, ...> 返回值类型 方法名(参数列表){
    //方法体
    ......
}


泛型方法可以定义在普通方法中,也可以定义在泛型类中。

当调用一个泛型方法时,在方法名前的尖括号中放入具体的类型:
String[] names = {"John", "Q", "Public"};
String middle = ArrayAlg.<String>getMiddle(names);
System.out.println(middle);//Q


在这种情况下(实际也是大多数情况)下,方法调用中可以省略<String>类型参数。编译器有足够的信息能够推断出所调用的方法。它用names的类型(即String[])与泛型类型T[]进行匹配并推断出T一定是String。也就是说,可以调用
String middle = ArrayAlg.getMiddle(names);


大多数情况下,对于泛型方法的类型引用没有问题。偶尔,编译器也会提示错误,此时需要编译错误报告。


4.类型变量的限定(Bounds for Type Variables)
有时候,类或方法需要对类型变量加以约束。
计算数组中最小的元素:
public class ArrayAlg{
    public static <T> T min(T[] a){
        if(a == null && a.length == 0){
            return null;
        }
        T smallest = a[0];
        for(int i = 0; i < a.length; i++){
            if(smallest.compareTo(a[i]) > 0){
                smallest = a[i];
            }
        }
        return smallest;
    }
}


这里存在一个问题。方法min中,变量smallest的类型为T,这意味着其可以是任何一个类型的对象,由此便不能确定T所属的类型是否有compareTo方法。因此此处会提示编译错误:
The method compareTo(T) is undefined for the type T


解决方案:将T限制为实现了Comparable接口(只含一个方法compareTo的标准接口)的类。可以通过对类型变量设置限定(bound)实现这一点:
public static <T extends Comparable> T min(T[] a){...}


实际上Comparable接口本身就是一个泛型类型。目前,我们忽略其复杂性以及编译器产生的警告。

现在,泛型的min方法只能被实现了Comparable接口的类(如String、Date等)的数组调用。

<T extends Bounding Type>

表示T应该是绑定类型(bounding type)的子类型(subtype)。T和绑定类型可以是类,也可以是接口。

一个类型变量或通配符可以有多个限定(multiple bounds),例如
T extrnds Comparable & Serializable

限定类型(bounding types)用“&”分隔,而逗号用来分隔类型变量(type variables)。

在Java的继承中,可以有任意多个接口超类型,但最多只有一个限定是类。如果有一个类作为限定,它必须是限定列表中的第一个。
(As with Java inheritance, you can have as many interface supertypes as you like, but at most one of the bounds can be a class. If you have a class as a bound, it must be the first one in the bounds list. )

例子:重新编写一个泛型方法minmax,该方法计算出泛型数字中的最小值和最大值,并返回Pair<T>。
package generic;

public class ArrayAlg1{
	public static <T extends Comparable> Pair<T> minmax(T[] a){
		if(a == null || a.length == 0 ){
			return null;
		}
		T min = a[0];
		T max = a[0];
		for(int i = 0; i < a.length; i++){
			if(min.compareTo(a[i]) > 0){
				min = a[i];
			}
			if(max.compareTo(a[i]) < 0){
				max = a[i];
			}
		}
		return new Pair<T>(min, max);
	}
	
	//test
	public static void main(String[] args){
		String[] words = {"Hello", "World", "very", "good"};
		Pair<String> mm = ArrayAlg1.minmax(words);
		System.out.println("min = " + mm.getFirst());
		System.out.println("max = " + mm.getSecond());
	}
}


5.泛型代码与虚拟机
虚拟机没有泛型类型对象——所有对象都属于普通类。

无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(raw type)。原始类型的名字就是移除类型参数(type parameters)后的泛型类型名。类型变量(type variables)被擦除(erased),并且被替换为它的限定类型(bounding types)(或者是Object对于无限定的变量)。

例如,Pair<T>的原始类型如下;
public class Pair{
    private Object first;
    private Object second;
    
    public Pair(Obejct first, Object second){
        this.first = first;
        this.second = second;    
    }
    
    public Object getFirst(){
        return this.first;    
    }
    
    public void setFirst(Object newValue){
        this.first = first;
    }
    
    public Object getSecond(){
        return this.second;
    }
    
    public void setSecond(Object newValue){
        this.second = newValue;
    }
}


因为T是一个无限定的变量(unbounded type variable),所以直接使用Object替换。
结果是一个普通的类,就好像泛型引入Java语言之前已经实现的那样。

在程序中可以包含不同类型的Pair,例如,Pair<String>或Pair<Integer>。而擦除类型后就变成原始的Pair类型了。






分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics