泛型原理、概念、使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MR_ChenT/article/details/83826980

泛型简介/原理

1、从JDK1.4开始是没有泛型的概念的,那怎么做呢?如下列代码
	List list = new ArrayList();
	list.add("string");
	list.add(11);
	String name = (String)list.get(0);
	Integer number = (Integer)list.get(1);

△这样子做有什么缺点呢
存的都是object,需要强转,转换时可能发生异常

2、JDK1.5以后的写法
	List<String> list = new ArrayList();
	list.add("string");
	list.add("string2");
	String cnBlogs = list.get(0);
	String myWebSite = list.get(1); 

总结: 泛型就是将类型参数化,其在编译时才确定具体的参数。在上面这个例子中,这个具体的类型就是 String。
可以看到我们在创建 List 集合的时候指定了 String 类型,这就意味着我们只能往 List 集合中存放 String 类型的数据。
而当我们指定泛型之后,我们去取出数据后就不再需要进行强制类型转换了,这样就减少了发生强制类型转换的风险。
这就是Sun公司为了JAVA语言更严格安全。

3、泛型的原理:

能看出这段代码的结果是什么吗?

		ArrayList<String> a = new ArrayList<String>();
		ArrayList<Integer> b = new ArrayList<Integer>();
		Class c1 = a.getClass();
		Class c2 = b.getClass();
		System.out.println(c1 == c2); 

△答案:是true,泛型只存在于编译阶段,而不存在于运行阶段。c1和c2获取到的都是java.util.ArrayList,Integer 只不过是 JVM在编译的时候会进行转换,编译后的文件时没有泛型概念的,也就是SUN严格规范了JAVA语言更安全。

△扩展:上面我们只是说了泛型在集合中的使用方式,但其实泛型的应用范围不仅仅只是集合,还包括类、方法、Map 接口等等

泛型的应用场景

泛型类
1、泛型类一般使用字母 T 作为泛型的标志
	public class GenericClass<T> {
	    private T object;
	    public T getObject() {
	        return object;
	    }
	    public void setObject(T object) {
	        this.object = object;
	    }
	}

△扩展: MAP类中也有标志 K-V

	public class GenericMap<K, V> {
	    private K key;
	    private V value;
	    public void put(K key, V value) {
	        this.key = key;
	        this.value = value;
	    }
	}
	public static void main(String[] args) {
	        GenericMap<Integer, String> team = new GenericMap<>();
	        team.put(1, "YaoMin");
	        team.put(2, "Me");
	        GenericMap<String, Integer> score = new GenericMap<>();
	        score.put("YaoMin", 88);
	        score.put("Me", 80);
	    }

△扩展: 那么像每次定义枚举类型,都是数值什么的那么我们怎么定义中文描述

不会动的加载中...
泛型方法
1、泛型方法一般也用字母 T 作为泛型的标志
	public class GenericMethod {
	    public static <T> T getObject(Class<T> clz) throws InstantiationException, IllegalAccessException{
	        T t = clz.newInstance();
	        return t;
	    }
	}
	public static void main(String[] args) throws Exception{
	    GenericMethod genericMethod = getObject(GenericMethod.class);
	    System.out.println("Class:" + genericMethod.getClass().getName());
	}

泛型集合 拉拉最顶上↑

泛型通配符

1、除了泛型类、泛型方法之外,泛型还有更加复杂的应用,如:
	List<? extends Number> list = new ArrayList();
	List<? super Number> list = new ArrayList();

△上面的 extends 和 super 关键字其实就是泛型的高级应用:泛型通配符。
△但在讲泛型通配符之前,我们必须对编译时类型和运行时类型有一个基本的了解,才能更好地理解通配符的使用。

	public class Tree {}
	public class Fruits extends Tree{}
	public class Banana extends Fruits{}
	public class Plate<T> {
	    T t;
	    public T get(){
	        return  t;
	    }
	    public void set(T t){
	        this.t=t;
	    }
	}
2、来看看泛型的劣优

(1)Error 泛型不支持向上转型

       /* Plate<Fruits> banana=new Plate<Banana>();*/

(2) ? extends 用法

	Plate<? extends Fruits> bananaExtens=new Plate<Banana>();
	/*//Error不允许写入 不管是父类还是子类
	bananaExtens.set(new Fruits());*/      
	/**就这一步,未运行时根本不知道你实例的那个子类,那 JVM 就干脆什么都不给放,避免出错。**/
	//只允许读取
	bananaExtens.get();

(3) ? super 用法 声明子类,其实例对象可以是父类或是父类以上的级别,例Object也是可以的

        Plate<? super Banana> bananaFruSuper=new Plate<Fruits>();
        Plate<? super Banana> bananaTreSuper=new Plate<Tree>();
	   //可以写入,但是只能写入声明对象中同级或者以下的类 子类,子子类
		//就这步,为运行时JVM既然不能知道实例对象是哪个,但是JVM能确定Banana的子类能转换成Banana,但是父类却不能转换()
        bananaTreSuper.set(new Banana());
        /*//都是高于声明对象级别
        bananaTreSuper.set(new Tree());
        bananaTreSuper.set(new Fruits());*/
        
        /*//Error也是高于对象级别
        bananaFruSuper.set(new Fruits());*/
        
         /* //Error 能取但是,只能用Object 来接收
         Banana b= bananaFruSuper.get();
         Fruits f= bananaFruSuper.get();
         */
         
         Object o= bananaFruSuper.get();

PESC原则

在泛型编程时,使用部分限定的形参时,<? super T>和<? extends T>的使用场景容易混淆,PECS原则可以帮助我们很好记住它们:
生产者(Producer)使用extends,消费者(Consumer)使用super。
留下一段代码加深印象(来自JDK 8 Collections.copy()源码)

public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest"); 

        if (srcSize < COPY_THRESHOLD ||
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
            for (int i=0; i<srcSize; i++)
                dest.set(i, src.get(i));
        } else {
            ListIterator<? super T> di=dest.listIterator();
            ListIterator<? extends T> si=src.listIterator();
            for (int i=0; i<srcSize; i++) {
                di.next();
                di.set(si.next());
            }
        }
}

参考于:
https://www.cnblogs.com/chanshuyi/p/deep_insight_java_generic.html

猜你喜欢

转载自blog.csdn.net/MR_ChenT/article/details/83826980