16 泛型详解

1、为什么要引入泛型

  • 主要是解决类型安全机制,如果引入了泛型,在编译期间没有产生编译时异常,则在运行时期绝对不会产生类转换异常。(泛型信息只存在于编译期间,不会带到运行里面去,会被擦除。)

  • 可以往集合存存储任何类型的元素,存的时候会自动装箱成Object类型。当我想从集合中取元素出来时,取出的都是Object类型的元素。当我想调用某些包装类的特有方法时就需要强转成对应类型的变量。如下例
    在这里插入图片描述
    Substring是String当中特有的方法,截取第1个到第2个字符。在不同类我们调用方法时都是要对象点方法名的。调用String类当中的方法时传的参数当然是字符类型的,但是我从集合里取出是Object类型的,Object类型包括String,大转小,需要强转。
    当我用了泛型之后,就可以指定集合里存什么类型的元素。(数组不支持泛型!)。

  • 很多时候,尤其在使用对象的多态性的时候,你不知道进到你数据结构中的对象到底是什么类型的,执行的时候就会出问题。泛型就是限定一个数据结构,或者一个方法参数只允许传入什么类型的对象。

2、泛型的使用

2、1泛型能作用哪些元素

  • 作用于类
  • 作用于接口
  • 作用于方法
  • 作用于构造器

2.1.1泛型作用于类

//定义一个泛型类(实际上泛型类是不存在的,因为运行期间
//此泛型信息已经被擦除)
class Cat<T,R>{	
//这里的T和R不代表哪个确定的类型,当我new这个类的对象的时候才再确定这个
//类存什么类型的元素,这个T其实就是形参,当new泛型类的对象时再确定实参。
//我们先创建一个泛型,创建对象的时候再指明泛型的类型
	T t;  //定义两个变量
	R r;
}
public static void main(String[] args){
//在创建对象时指明泛型类型
//因为我定义的泛型类有两个形参T和R,所以实参也要有两个。
	Cat<String,Integer> cat1 = new Cat<>();//new Cat<String>()的缺省
	cat1.t = "小小";
	cat1.r = 5;
	Cat<String,String> cat2 = new Cat<>();
	cat2.t = "大大";
	cat2.r = "七七";
}

在这里插入图片描述

  • 为什么静态元素不能用泛型?因为不存在静态类,而泛型是依赖类的。
  • Cat<String,Integer>与Cat<String,String>都属于同一个类Cat,这也说明泛型类是不存在的。

2.1.2泛型作用于接口

语法

interface 接口名<T,R,S...>
{
    T next();
}

在这里插入图片描述

  • 实现泛型接口的类,有两种方式:
    – 给出了泛型接口的类型实参,class可以是普通类。如
    class FX32 implements FX31{}

    – 未给出泛型接口的类型实参,则类就必须是泛型类。如
    class FX33 implements FX31 {}

interface Generator<T>//定义一个泛型接口
{
	public T next();//定义一个T类型的方法。当实现接口时会给接口中的T实参,此时这个方法的类型也就确定了
}

class FruitGenerator implements Generator<String>//
{
	String []fruits=new String[] {"apple","Pear","banana"};
	@Override
	public String next() {
		
		//数组fruits的下标[0---2];
		int index=new Random().nextInt(3);//为什么是3?3的话是数组中的三个元素,2的话就是前两个元素
		return fruits[index];		
	}	
}

class NumGenerator implements Generator<Integer>
{
	@Override
	public Integer next() {
		return null;	
	}
}

class GenericCls<T> implements Generator<T>//未给出泛型接口的类型实参,则class就必须是泛型类。
{
	@Override
	public T next() {
		// TODO Auto-generated method stub
		return null;
	}
}

public class MainTest {
	public static void main(String[] args) {
		
		
		FruitGenerator generator=new FruitGenerator();
		//创建FruitGenerator对象然后调用FruitGenerator里的方法
		System.out.println(generator.next());
		
		//Generator<String> generator=new FruitGenerator();
		//GenericCls<Integer> t=new GenericCls<>()
	}
}

泛型的上下限以及泛型通配符

泛型作用于方法

  • 为什么引入泛型方法
    下面的代码:这个方法的功能非常有限,它只能将 Object[]数组的元素复制到元素为Object类型的集合
public class MainTest {
	//定义一个方法,形式参数为Object类型的数组a,和只存Objec类型的集合b
	public static void fromArrayToCollection(Object[] a,Collection<Object> b)
	//这里的泛型不能用通配符,用了通配符就不能add了
	{
		//将a数组的数据复制到b集合对象
		for(Object v:a)
		{
			b.add(v);//存到b集合去
		}
	}
	public static void main(String[] args) {
		//我们定义的这个方法有一定的局限性,就是只能处理Object类型的数据,
		//下面这段注释的代码因为数据类型不兼容会报错
		/*Integer []a= {1,2,3};
		List<Integer> b=new ArrayList<Integer>();
		fromArrayToCollection(a,b);*/
		
		Object []a= {1,2,3};
		List<Object> b=new ArrayList<Object>();
		fromArrayToCollection(a,b);
	}
}

为了

如何定义一个泛型方法

修饰符 <T,R…> 返回值类型 方法名(形参列表){}

泛型方法的应用

泛型方法,是在调用方法的时候指明泛型的具体类型

public class ObjectTool {  

    public <T> void show(T t){//定义一个泛型方法
        System.out.println(t);
    }
}
public class ObjectToolDemo {

    public static void main(String[] args) {
        ObjectTool ot = new ObjectTool();

        ot.show("张三");

        ot.show(20);

        ot.show(true);

下面这部分代码属于转载,为了更好地理解。

public class GenericTest {
   //这个类是个泛型类,在上面已经介绍过
   public class Generic<T>{     
        private T key;

        public Generic(T key) {
            this.key = key;
        }

        //我想说的其实是这个,虽然在方法中使用了泛型,但是这并不是一个泛型方法。
        //这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
        //所以在这个方法中才可以继续使用 T 这个泛型。
        public T getKey(){
            return key;
        }

        /**
         * 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"
         * 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。
        public E setKey(E key){
             this.key = keu
        }
        */
    }

    /** 
     * 这才是一个真正的泛型方法。
     * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
     * 这个T可以出现在这个泛型方法的任意位置.
     * 泛型的数量也可以为任意多个 
     *    如:public <T,K> K showKeyName(Generic<T> container){
     *        ...
     *        }
     */
    public <T> T showKeyName(Generic<T> container){
        System.out.println("container key :" + container.getKey());
        //当然这个例子举的不太合适,只是为了说明泛型方法的特性。
        T test = container.getKey();
        return test;
    }

    //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
    public void showKeyValue1(Generic<Number> obj){
        Log.d("泛型测试","key value is " + obj.getKey());
    }

    //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
    //同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
    public void showKeyValue2(Generic<?> obj){
        Log.d("泛型测试","key value is " + obj.getKey());
    }

     /**
     * 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
     * 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
     * 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
    public <T> T showKeyName(Generic<E> container){
        ...
    }  
    */

    /**
     * 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
     * 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
     * 所以这也不是一个正确的泛型方法声明。
    public void showkey(T genericObj){

    }
    */

    public static void main(String[] args) {


    }
}

下面这部分代码属于转载,为了更好地理解。

public class GenericFruit {
    class Fruit{
        @Override
        public String toString() {
            return "fruit";
        }
    }

    class Apple extends Fruit{
        @Override
        public String toString() {
            return "apple";
        }
    }

    class Person{
        @Override
        public String toString() {
            return "Person";
        }
    }

    class GenerateTest<T>{
        public void show_1(T t){
            System.out.println(t.toString());
        }

        //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
        //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
        public <E> void show_3(E t){
            System.out.println(t.toString());
        }

        //在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
        public <T> void show_2(T t){
            System.out.println(t.toString());
        }
    }

    public static void main(String[] args) {
        Apple apple = new Apple();
        Person person = new Person();

        GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
        //apple是Fruit的子类,所以这里可以
        generateTest.show_1(apple);
        //编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person
        //generateTest.show_1(person);

        //使用这两个方法都可以成功
        generateTest.show_2(apple);
        generateTest.show_2(person);

        //使用这两个方法也都可以成功
        generateTest.show_3(apple);
        generateTest.show_3(person);
    }
}

猜你喜欢

转载自blog.csdn.net/csdn00er/article/details/107741875