泛型(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)没有错误检查,可以添加任何对象。

泛型提供了一个更好的解决方案:[b]类型参数(type parameters)[/b]
[b]ArrayList类有一个类型参数用来指示元素的类型[/b]:
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)就是具有一个或多个[b]类型变量(type variables)[/b]的类。
例如: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类引入了一个[b]类型变量T[/b],用尖括号(<>)括起来,并放在类名后面。
[b]泛型类可以有多个类型变量[/b]:
public class Pair<T, U>{...}

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

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

用[b]具体的类型[/b]替换[b]类型变量[/b]就可以[b]实例化泛型类型[/b],例如:
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.泛型代码与虚拟机
[b]虚拟机没有泛型类型对象——所有对象都属于普通类。[/b]

无论何时定义一个泛型类型,都自动提供了一个相应的原始类型(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类型了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值