Java 泛型,了解这些就够用了。

此文目录:

  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;

    //  省略构造方法和 get,set方法。

}

  定义泛型方法:

  

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>();
        result.setResultCode(0);
        result.setResultMsg("success");
        result.setResultObject(new User());

      ApiResult<List<User>> result2=new ApiResult<List<User>>();
      result.setResultCode(0);
      result.setResultMsg("success");
      result.setResultObject(userList);

      ApiResult<Integer> result3=new ApiResult<Integer>();
      result3.setResultCode(0);
      result3.setResultMsg("success");
      result3.setResultObject(99);

    //泛型方法使用

      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

  

 三、类型擦除

  先看一个例子,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  查看编译之后的代码如下

发现没有,根本没有<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原则

五、PECS原则

 先看例子:

此处定义三个类,spiring,summer继承seasonpublic 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)
如果既要存又要取,那么就不要使用任何通配符。
时间: 2024-11-04 13:47:22

Java 泛型,了解这些就够用了。的相关文章

Java泛型的协变

在上篇<Java泛型的基本使用>这篇文章中遗留下面问题,即将子类型也能加入到父类型的泛型中.要实现这样的功能必须借助于协变. 实验准备 如今在上篇文章展示的Decorator类型的基础上,添加一些代码,如代码清单1所看到的. 代码清单1 /** * * 描 述:Exp2使用br/> * 作 者:jiaan.gja<br/> * 历 史: (版本号) 作者 时间 凝视 <br/> * @param itemList */ public void doDecorate

Java泛型中的PECS原则

今天在写代码的时候使用到了这样一个方法签名: public void foo(Map<String, String> map); 在写这个参数的时候正好在想一些关于泛型的东西,于是: public void foo(Map<? extends String, ? extends String> map); 这两种写法有什么区别呢?记得以前和同学讨论过这个问题,但后来没有记下来,渐渐又淡忘了.今天又去翻了好多资料,总算找到一些可以参考的,赶紧记在这里方便以后温故知新啦.好了,言归正传

2017.4.5 Java 泛型

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 假定我们有这样一个需求:写一个排序方法,能够对整形数组.字符串数组甚至其他任何类型的数组进行排序,该如何实现? 答案是可以使用 Java 泛型. 使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序.然后,调用该泛型方法来对整型数组.浮点数数组.字符串数组等进行排

关于Java泛型的使用

在目前我遇到的java项目中,泛型应用的最多的就属集合了.当要从数据库取出多个对象或者说是多条记录时,往往都要使用集合,那么为什么这么使用,或者使用时有什么要注意的地方,请关注以下内容. 感谢Windstep. 原文链接:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 原文标题:Java总结篇系列:Java泛型 (我的第一篇水文,233)

java 泛型 窜讲

一.为什么使用泛型      复用性:泛型的本质就是参数化类型,因而使用编写的泛型代码可以被许多不同类型的对象所复用.      安全性:在对类型Object引用的参数操作时,往往需要进行显式的强制类型转换.这种强制类型转换需要在运行时才能被发现是否转换异常,通过引入泛型能将在运行时才能检查类型转换,提前到编译时期就能检查. 二.自定义泛型 java中自定义泛型分为三种:泛型类.泛型接口.泛型方法. 下面使用一个案例演示泛型类.泛型方法,泛型接口类似,所以不再演示. // 自定义泛型类publi

1月21日 - (转)Java 泛型

java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象. 因为 M

Java泛型_上界extends_下界super

?Java泛型_上界extends_下界super ? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T或是T的子类 <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型(T)的超类型(父类型),直至Object 当使用 Upper Bound 通配符时 如下代码, /**  * 代码中通配符<?> 是 <? extends Object> 的简写  *  * @param list

C++泛型 &amp;&amp; Java泛型实现机制

C++泛型 C++泛型跟虚函数的运行时多态机制不同,泛型支持的静态多态,当类型信息可得的时候,利用编译期多态能够获得最大的效率和灵活性.当具体的类型信息不可得,就必须诉诸运行期多态了,即虚函数支持的动态多态. 对于C++泛型,每个实际类型都已被指明的泛型都会有独立的编码产生,也就是说list<int>和list<string>生成的是不同的代码,编译程序会在此时确保类型安全性.由于知道对象确切的类型,所以编译器进行代码生成的时候就不用运用RTTI,这使得泛型效率跟手动编码一样高.

java 泛型详解(普通泛型、 通配符、 泛型接口,泛型数组,泛型方法,泛型嵌套)

JDK1.5 令我们期待很久,可是当他发布的时候却更换版本号为5.0.这说明Java已经有大幅度的变化.本文将讲解JDK5.0支持的新功能-----Java的泛型. 1.Java泛型  其实Java的泛型就是创建一个用类型作为参数的类.就象我们写类的方法一样,方法是这样的method(String str1,String str2 ),方法中参数str1.str2的值是可变的.而泛型也是一样的,这样写class Java_Generics<K,V>,这里边的K和V就象方法中的参数str1和st