Java泛型

来源:互联网 发布:php用户信息管理系统 编辑:程序博客网 时间:2024/06/05 00:33

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()));  //②    }}

上述程序②处引发ClassCastException异常


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、设定类型通配符的上限

//定义一个抽象类Shapepublic abstract class Shape {    public abstract void draw(Canvas c);}//定义一个Shape的子类Circlepublic class Circle extends Shape {    public void draw(Canvas c) {        System.out.println("在画布"+c+"上画一个圆");    }}//定义一个Shape的子类Rectanglepublic 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> 返回值类型 方法名(形参列表){     //方法体}
0 0
原创粉丝点击