美文网首页
【JAVA】浅谈Java范型

【JAVA】浅谈Java范型

作者: Y了个J | 来源:发表于2018-09-21 10:48 被阅读12次

1.Java泛型是什么?
2.通常的泛型的写法示例
3.类型擦除
4.为什么要使用Java泛型
5.通过示例了解PECS原则

一、Java泛型是什么?

  • 官方定义
    泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
    这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

  • 通俗解释
    通俗的讲,泛型就是操作类型的 占位符,即:假设占位符为T,那么此次声明的数据结构操作的数据类型为T类型。

二、通常的泛型写法示例

  • T 类型,用于泛型类或者泛型方法
泛型类定义:
public class ApiResult<T> {
   int resultCode;
    String resultMsg;
    T resultObject;
}
定义泛型方法:
public class JsonUtil {
    public <T> T  str2Json(String jsonText,Class target){
        T result=null;
        //....parse to json 
        return result;
    }
}

使用:

//泛型类使用
ApiResult<User> result=new ApiResult<User>();
//泛型方法使用
String userJsonText="....省略",dogJsonText="....省略";;
User u=JsonUtil.str2Json(jsonText,User.class);
User u=JsonUtil.str2Json(jsonText,Dog.class);
  • K,V类型,类似Map接口。
public class ResultMap<K,V> {
    private K key;
    private V value;
  
    //省略 set ,get  方法
    public void put(K key,V value){
        this.key=key;
        this.value=value;
    }
}

使用:
ResultMap<String,User> resultMap=new ResultMap<>();
resultMap.put("currentUserKey", new User());
  • ?extends 类型
    <? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类

  • ?supper 类型
    <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object

  • Class<T>和Class<?>

People people = (People) Class.forName("com.demo.fanxing.People").newInstance();

看到了么,需要强转,如果反射的类型不是People类,就会报
java.lang.ClassCastException错误。
使用Class<T>泛型后,不用强转了

public class Test {
    public static <T> T createInstance(Class<T> clazz) 
      throws IllegalAccessException, InstantiationException {
        return clazz.newInstance();
    }

    public static void main(String[] args)  
      throws IllegalAccessException, InstantiationException  {
            Fruit fruit= createInstance(Fruit .class);
            People people= createInstance(People.class);
    }
}

那Class<T>和Class<?>有什么区别呢?
Class<T>在实例化的时候,T要替换成具体类
Class<?>它是个通配泛型,?可以代表任何类型,主要用于声明时的限制情况

例如可以声明一个

public Class<?> clazz;

但是你不能声明一个

public Class<T> clazz;

三、类型擦除
先看一个例子,Operate类如下:

public class Operate {
    public static void main(String[] args) {
        List<String> names=new ArrayList<String>();
        names.add("Jack");
        names.add("Tom");
        names.add("peter");
        for(String name:names){
            System.out.println("wellcome:"+name);
        }
    }
}

其对应的class文件反编译之后,我们使用java-gui反编译.exe 查看编译之后的代码如下

public class Operate {
    public static void main(String[] args) {
        List names=new ArrayList();
        names.add("Jack");
        names.add("Tom");
        names.add("peter");
        for(String name:names){
            System.out.println("wellcome:"+name);
        }
    }
}

发现没有,根本没有<String> 这一部分了。这个限制为String类型的泛型被“擦除”了。写代码的时候,泛型会做校验,类型不对应的,无法add,但是编译之后边去掉了泛型类型。

四、什么要使用Java泛型

在上面 第三部分介绍了“类型擦除”之后,在来说为什么要使用Java泛型就比较好说了。这里的泛型就相当于“约法三章”,先给你定好“规矩”,我这个List<String> 就是用来操作
String类型的,你插入Person对象就不行。说白了就是为了类型安全。所以其好处有:

类型安全:通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。

消除强制类型转换:

//该代码不使用泛型:
List li = new ArrayList();
li.put(new Integer(3));
Integer i = (Integer) li.get(0);

//该代码使用泛型:
List<Integer> li = new ArrayList<Integer>();
li.put(new Integer(3));
Integer i = li.get(0);

五、PECS原则
先看例子:

此处定义三个类,spiring,summer继承season
public class Season {
  //.....  
}

public class Spring extends Season {
  //......  
}

public class Summer extends Season {
 //.......
}
List<? extends Season> list1=new ArrayList<>();
list1.add(new Spring());这里编译不通过,因为编译器无法确定list所持有的类型。

List<? extends Season> list2=new ArrayList<Spring>();
list2.add(new Spring());也是无法通过编译
通过上文,我们知道 ?extends Season表示可以接收的类型为 Seaon 或者其子类。
但是此处不行,因为可能传入进来的是spring,或者summer,编译器无法确定具体传递进来的是什么,
所以无法安全的向其中添加对象,但是它可以接收子类类型 的赋值。如下:

List<Spring> list3=new ArrayList<Spring>();
List<? extends Season> list4=list3;
这里和上面的list2做对比,无法直接add spring类型的对象,但是可以直接将spring类型的list赋值。
        
List<Season> seasons=new ArrayList<Season>();
List<? super Spring> spring=seasons;
spring.add(new Spring());//ok
//        spring.add(new Summer());//error
//        spring.add(new Season());//error
//        spring.add(new Object());//error
List<? super Season> sea=new ArrayList<>();
sea.add(new Spring());//ok
sea.add(new Summer());//ok
sea.add(new Season());//ok
sea.add(new Object());//error
        
List<? super Spring> spring=new ArrayList<>();
spring.add(new Spring());//ok
//        spring.add(new Summer());//error
//        spring.add(new Season());//error
//        spring.add(new Object());//error

这里 ,PECS原则 如下:
如果要从集合中读取类型T的数据,并且不能写入,可以使用 ? extends 通配符;(Producer Extends)
如果要从集合中写入类型T的数据,并且不需要读取,可以使用 ? super 通配符;(Consumer Super)
如果既要存又要取,那么就不要使用任何通配符。

相关文章

  • 【JAVA】浅谈Java范型

    1.Java泛型是什么?2.通常的泛型的写法示例3.类型擦除4.为什么要使用Java泛型5.通过示例了解PECS原...

  • CoreJava笔记 - 范型程序设计(2)

    范型代码和Java虚拟机 关键知识:类型擦除Java的范型是编译器层次的范型,而在Java虚拟机上并没有范型类。在...

  • Java编程思想之范型(1)

    Java范型是在Java5.0(1.5)版本中提出的,虽然Oracle已经发布了Java 1.9,Java 12的...

  • Java 范型

    范型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。范型的本质是参数化类型,也就是说所操作...

  • java范型

    原始文件: https://pan.baidu.com/s/1cLBYSe , 提取码: eccp

  • java范型

    Java 语言中,引入泛型实乃为一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库...

  • Java 范型

    假定有一个这样的需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?答案是...

  • Effective Java 3rd-----Chapter 5

    Chapter 5 Generics 范型 SINCE Java 5, generics have been a ...

  • CoreJava笔记 - 范型程序设计(5)

    反射与范型 由于类型擦除,反射无法得到关于范型类型参数的信息。 范型的Class类在Java的反射库中,Class...

  • Generic 范型 - type parameter

    范型简介 从JDK 5.0开始,范型作为一种新的扩展被引入到了java语言中。 有了范型,我们可以对类型(type...

网友评论

      本文标题:【JAVA】浅谈Java范型

      本文链接:https://www.haomeiwen.com/subject/qjkanftx.html