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

Java泛型

2016-05-01 13:08 531 查看

Why

JDK1.5增加泛型支持在很大程度上是为了让集合能记住其元素的数据类型。在没有泛型前,一旦把一个对象“丢进”java集合中,集合就会忘记对象的类型,把所有对象当成Object类型处理。当程序从集合中取出对象后,就需要进行强制类型转换,这种强制类型转换不仅使代码臃肿,而且容易引起ClassCastException异常。


增加了泛型支持后的集合的优点:

可以记住集合中元素的类型,并可以在编译时检查集合中元素的类型,如果试图向集合中添加不满足类型要求的对象,编译器就会提示错误;

可以让代码更加简洁,程序更加健壮;

除此之外,Java泛型还增强了枚举类、反射等方面的功能。

总结起来就是在编译时会检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码重用率。

import java.util.List;
import java.util.ArrayList;

public class Solution {
public static void main(String[] args) {
//创建一个只想保存字符串的List集合
List strList = new ArrayList();

strList.add("疯狂java讲义");
strList.add("疯狂Android讲义");
//“不小心”把一个Integer对象“丢进”了集合
strList.add(5);    //①

strList.forEach(str->System.out.println(((String)str).length()));  //②
}
}




What、When、Where

所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定。

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类泛型接口泛型方法

Java引入泛型的好处是安全简单。

规则限制

泛型的类型参数只能是类类型(包括自定义类),不能是简单类型;

同一种泛型可以对应多个版本(因为参数类型是不确定的),不同的版本的泛型类实例是不兼容的;

泛型的类型参数可以有多个;

泛型的参数类型可以使用extends语句,例如< T extends superclass>。习惯上称为“有界类型”;

泛型的参数类型还可以是通配符类型。例如Class< ? > classType = Class.forName(“java.lang.String”).

How

1、定义泛型接口、类

public class Apple<T> {
private T info;
public Apple(){}

public Apple(T info) {
this.info = info;
}
public void setInfo(T info) {
this.info = info;
}
public T getInfo() {
return this.info;
}

public static void main(String[] args) {
Apple<String> a1 = new Apple<>("苹果");
System.out.println(a1.getInfo());
Apple<Double> a2 = new Apple<>(5.67);
System.out.println(a2.getInfo());
}
}


注意:当创建泛型类声明自定义类,为该类定义构造函数时,构造函数名还是原来的类名,不要增加泛型声明。例如,为Apple< T>类定义构造函数,其构造函数名依然是Apple,而不是Apple< T>!调用该构造函数时却可以使用Apple< T>的形式,当然应该为T形参传入实际的类型参数。Java7提供了菱形语法,允许省略< >中的类型参数。

事实上,并不存在泛型类,例如,

import java.util.List;
import java.util.ArrayList;

public class Test {

public static void main(String[] args) {

List<String> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
System.out.println(l1.getClass()==l2.getClass());
}
}


结果为true,因为不管泛型的实际类型参数是什么,他们在运行时总有同样的类(class)处理,在内存中也只占用一块内存空间,因此在静态方法、静态初始化块或者静态变量声明和初始化中不允许使用类型形参。比如下面代码:

public class R<T> {
//下面代码错误,不能在静态变量声明中使用类型参数
static T info;
T age;
public void foo(T msg){}
//下面代码错误,不能在静态方法声明中使用类型参数
public static void bar(T msg){}
}


由于系统并不会真正生成泛型类,所以intanceof运算符后不能使用泛型类。例如,下面代码是错误的:

java.util.Collection<String> cs = new java.util.ArrayList<>();
//下面代码编译时引起错误
if(cs intanceof java.util.ArrayList<String>(){}


2、设定类型通配符的上限

//定义一个抽象类Shape
public abstract class Shape {
public abstract void draw(Canvas c);
}
//定义一个Shape的子类Circle
public class Circle extends Shape {
public void draw(Canvas c) {
System.out.println("在画布"+c+"上画一个圆");
}
}
//定义一个Shape的子类Rectangle
public class Rectangle extends Shape {
public void draw(Canvas c) {
System.out.println("在画布"+c+"上画一个矩形");
}
}
public class Canvas {
public void drawAll(List<? extends Shape> shapes) {
for(Shape s : shapes) {
s.draw(this);
}
}
}


List< ? extends Shape > 是受限制通配符的例子,此处的问号(?)代表一个未知的类型,但是此处的这个未知类型一定是Shape的子类型,因此可以把Shape称为这个通配符的上限。

3、定义泛型方法

泛型方法的语法格式如下:

修饰符 <T,S> 返回值类型 方法名(形参列表){
//方法体
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: