黑马程序员-泛型

---------------------- ASP.Net+Android+IOS开发</a>、 .Net培训、期待与您交流! ----------------------


泛型入门

在没有泛型之前,Java集合有个缺点,当我们把一个对象丢进集合里后,集合就会忘记这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型(其运行时类型没变)

什么时候使用泛型?
当操作的引用数据类型不确定的时候

泛型的好处?
将运行时期的问题,ClassCastException转到了编译时期
避免了强制类型转换的麻烦和代码臃肿

import java.util.List;
import java.util.ArrayList;
/**
不使用泛型的集合容易引起ClassCastException
*/

class ListError
{
    public static void main(String[] args)
    {
        //创建一个只想保存字符串的List集合
        List strList = new ArrayList();
        strList.add("aaaa");
        strList.add("bbbb");
        strList.add("cccc");
        //不小心把一个Integer对象丢进了集合
        strList.add(6);
        for(int i = 0; i < strList.size(); i++)
        {
            //因为List里取出的全部是Object,所以必须强制类型转换
            //最后一个元素将出现ClassCastException异常
            String str = (String)strList.get(i);
        }
    }
}


泛型术语

泛型术语,以ArrayList<E>为例:
整个ArrayList<E>称为:泛型类型
ArrayList称为:原始类型(raw type)
<>念:typeof
ArrayList<E>中的E称为:类型参数或类型变量

整个ArrayList<Integer>称为:参数化的类型(ParameterizedType)
ArrayList<Integer>中的Integer称为:实际类型参数或类型参数的实例


泛型的注意事项

泛型不考虑类型参数的继承关系
ArrayList<String> a = new ArrayList<Object>();    //错误,不写<Object>可以(兼容老版本),写了就是明知故犯
ArrayList<Object> a = new ArrayList<String>();    //错误

泛型的类型参数只表示引用数据类型

import java.util.ArrayList;
/**
泛型的内部原理
*/

class GenericTest
{
    public static void main(String[] args) throws Exception
    {
        ArrayList<Integer> al1 = new ArrayList<Integer>();

        ArrayList<String> al2 = new ArrayList<String>();

        //证明泛型只存在于源文件
        System.out.println(al1.getClass() == al2.getClass());

        //利用反射向集合中添加非泛型限定的对象
        al1.getClass().getMethod("add", Object.class).invoke(al1, "abc");
        System.out.println(al1.get(0));
    }
}


泛型类

当一个类不确定要处理的引用类型数据时,可以定义泛型类,在创建对象时指定具体类型

/**
定义并使用泛型类
*/
 
//定义GenericClass时使用了泛型声明
class GenericClass<T>
{
    //使用类型参数T定义属性
    private T info;
 
    public GenericClass(){}
 
    //使用类型参数T作为构造函数的形参
    public GenericClass(T info)
    {
        this.info = info;
    }
 
    //使用类型参数T作为方法的形参
    public void setInfo(T info)
    {
        this.info = info;
    }
 
    public T getInfo()
    {
        return this.info;
    }
}
 
//测试泛型类
class GenericClassTest
{
    public static void main(String[] args)
    {
        //实际类型参数是String,所以构造器的形参是String类型
        GenericClass<String> a1 = new GenericClass<String>("泛型类");
        System.out.println(a1.getInfo());
 
        //实际类型参数是Double,所以构造器的形参是Double类型
        GenericClass<Double> a2 = new GenericClass<Double>(5.67);    //自动装箱
        System.out.println(a2.getInfo());
    }
}


泛型方法

/**
泛型方法
*/

class GenericMethod<T>
{
    //在方法中使用类型参数T作为形参,当创建Generic对象的时候,T被确定,而方法只能接收被确定的T类型
    public void print(T msg)
    {
        System.out.println(msg);
    }

    //如果在方法上定义泛型,就不存在上面的问题
    public <E> void show(E msg)
    {
        System.out.println(msg);
    }
}

class GenericMethodTest
{
    public static void main(String[] args)
    {
        GenericMethod<String> gm = new GenericMethod<String>();
        gm.print("hello");    //print方法此时只能接收字符串参数
        gm.show(123456);    //方法上定义泛型,不受类上泛型的影响
    }
}

在静态方法上使用泛型
/**
泛型类上的类型参数T只有在建立对象时才会被确定,所以无法在静态方法和属性上使用T
如果要在静态方法上使用泛型,就不可以使用泛型类上的类型参数,可以将泛型定义在静态方法上
*/

class GenericMethod<T>
{
    //下面代码错误,无法在静态中访问非静态T
    //public static T info;

    //下面代码错误,无法从静态方法中访问非静态T
    //public static void setInfo(T msg){};

    //下面代码是允许的,因为在使用静态方法时就可以确定泛型E
    public static <E> void print(E msg)
    {
        System.out.println(msg);
    }
}

class GenericMethodTest2
{
    public static void main(String[] args)
    {
        GenericMethod.print("hello");
    }
}

泛型接口

/**
泛型接口
*/

//泛型接口
interface GenericInterface<T>
{
    public void show(T t);
}

//实现接口,可以指定泛型接口的类型参数(另见例子2)
class GenericInterfaceImpl implements GenericInterface<String>
{
    public void show(String t)
    {
        System.out.println(t);
    }
}

//测试
class GenericInterfaceTest
{
    public static void main(String[] args)
    {
        GenericInterfaceImpl gifi = new GenericInterfaceImpl();
        gifi.show("hello");
    }
}


泛型限定

通配符:<?>

上限定:<? extends E>
限定类型参数只能是E或E的子类

下限定:<? super E>
限定类型参数只能是E类型或E类型的父类型

如TreeSet<E>构造方法:TreeSet(Comparator<? super E> comparator)
限定Comparator的类型参数只能为E或者E的父类

import java.util.ArrayList;
import java.util.Iterator;
/**
上限定:<? extends E>

下限定:<? super E>
使用下限定时,由于无法知道父类中存在什么特有方法,所以?代表的是Object类型,如下:
*/

class Person
{
    private String name;

    Person(String name)
    {
        this.name = name;
    }

    public String getName()
    {
        return name;
    }
}

//Student继承Person
class Student extends Person
{
    Student(String name)
    {
        super(name);
    }

    public void show()
    {
        System.out.println("hello");
    }
}

class Wildcards2
{
    public static void main(String[] args)
    {
        ArrayList<Person> al1 = new ArrayList<Person>();
        al1.add(new Person("person1"));
        al1.add(new Person("person2"));
        al1.add(new Person("person3"));
        print(al1);

        ArrayList<Student> al2 = new ArrayList<Student>();
        al2.add(new Student("student1"));
        al2.add(new Student("student2"));
        al2.add(new Student("student3"));
        print(al2);

        System.out.println("------------");
        print2(al1);
        print2(al2);
    }

    //限定ArrayList集合中的类型参数只能是Person或Person的子类
    public static void print(ArrayList<? extends Person> al)
    {
        Iterator<? extends Person> it = al.iterator();
        while(it.hasNext())
        {
            System.out.println(it.next().getName());
        }
    }

    //限定ArrayList集合中的类型参数只能是Student或Student的父类
    //但取出集合中的元素的类型是Object,无法使用Student或者其父类的特有方法
    public static void print2(ArrayList<? super Student> al)
    {
        Iterator<? super Student> it = al.iterator();
        while(it.hasNext())
        {
            System.out.println(it.next());
        }
    }
}




---------------------- ASP.Net+Android+IOS开发</a>、 .Net培训、期待与您交流! ----------------------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值