Java基础 --- 泛型 Generics

  • 为什么需要泛型
  • 泛型
  • Bounds for Type Variable
  • Java虚拟机如何处理泛型 --- 泛型擦除
  • Restrictions and Limitations
  • 通配符类型 --- Wildcard Types

为什么需要泛型

  • 在没有泛型类之前, Java用继承解决泛型问题. 也就是利用Object类
// before generic classes
public class ArrayList {private Object[] elementData. . .public Object get(int i) { . . . }public void add(Object o) { . . . }
}
  • 这样有两个问题
  • 当获取值时必须要转型
    ArrayList files = new ArrayList();
    String filename = (String) files.get(0);
  • 没有error checking, 可以给这个arraylist加入任何类(因为所有类的父类都是object)
    files.add(new File(". . .")); This call compiles and runs without error. Elsewhere, casting the result of get to a String will cause an error
  • 泛型提供了type parameter, 这样就可以给泛型指定一个类型, 代码更易读, 不用转型并且有error checking
    ArrayList<String> files = new ArrayList<String>();

泛型

泛型类

public class Pair<T> {private T first;private T second;public Pair() { first = null; second = null; }public Pair(T first, T second) { this.first = first; this.second = second; }public T getFirst() { return first; }public T getSecond() { return second; }public void setFirst(T newValue) { first = newValue; }public void setSecond(T newValue) { second = newValue; }
}

泛型方法

class ArrayAlg {public static <T> T getMiddle(T... a) {return a[a.length / 2];}
}

Bounds for Type Variable

class ArrayAlg { public static <T> T min(T[] a) // almost correct  {if (a == null || a.length == 0) return null;T smallest = a[0];for (int i = 1; i < a.length; i++)if (smallest.compareTo(a[i]) > 0) smallest = a[i];return smallest;}
}
  • 上面的代码, T可以是任何类, 但是.compareTo方法只有实现了Comparable接口才有
  • 如何确保 T 一定实现了Comparable接口
  • 可以给type variable限定bounds, 也就是限定T必须继承某个父类或者接口
    public static <T extends Comparable> T min(T[] a)
  • 也可以限定多个接口 T extends Comparable & Serializable
  • 虽然bound可以是接口也可以是类, 但是最多只能限定一个类, 并且必须放在bounds list的第一个. 接口可以限定多个
class ArrayAlg
{/**Gets the minimum and maximum of an array of objects of type T.@param a an array of objects of type T@return a pair with the min and max value, or null if a isnull or empty*/public static <T extends Comparable> Pair<T> minmax(T[] a){if (a == null || a.length == 0) return null;T min = a[0];T max = a[0];for (int i = 1; i < a.length; i++){if (min.compareTo(a[i]) > 0) min = a[i];if (max.compareTo(a[i]) < 0) max = a[i];}return new Pair<>(min, max);}
}

Java虚拟机如何处理泛型 — 泛型擦除

泛型擦除 — Type Erase

  • Java 中的泛型只有在编译阶段存在,在代码运行的时候是没有泛型的,这也被称为泛型擦除
  • 虚拟机会对泛型代码进行泛型擦除, 也就是将所有的类型 T 替换成raw type, raw type就是bound list中的第一个类型, 或者是Object类型 如果没有bounds list

无bound list的情况

//执行完Type Erasure的代码
public class Pair
{private Object first;private Object second;public Pair(Object first, Object second) {this.first = first;this.second = second;}public Object getFirst() { return first; }public Object getSecond() { return second; }public void setFirst(Object newValue) { first = newValue; }public void setSecond(Object newValue) { second = newValue; }
}

有bound list的情况

public class Interval<T extends Comparable & Serializable> implements Serializable
{private T lower;private T upper;. . .public Interval(T first, T second){if (first.compareTo(second) <= 0) { lower = first; upper = second; }else { lower = second; upper = first; }}
}
public class Interval implements Serializable
{private Comparable lower;private Comparable upper;. . .public Interval(Comparable first, Comparable second) { . . . }
}

类型转换

Pair<Employee> buddies = . . .;
Employee buddy = buddies.getFirst();
  • 经过泛型擦除之后buddies.getFirst返回的是Object类型, 编译器自动进行类型转换, 转为Employee类型

桥接方法 — bridge Method

public class Pair<T> {private T first;private T second;public Pair() { first = null; second = null; }public Pair(T first, T second) { this.first = first; this.second = second; }public T getFirst() { return first; }public T getSecond() { return second; }public void setFirst(T newValue) { first = newValue; }public void setSecond(T newValue) { second = newValue; }
}
class DateInterval extends Pair<LocalDate>
{public void setSecond(LocalDate second){if (second.compareTo(getFirst()) >= 0)super.setSecond(second);}. . .
}

泛型擦除之后

class DateInterval extends Pair // after erasure
{public void setSecond(LocalDate second) { . . . }. . .
}

这样DataInterval会有两个setSecond方法

  1. public void setSecond(Object second) 从pair继承
  2. public void setSecond(LocalDate second)

这是两个完全不同的方法, 因为参数不一样. 但是实际上应该是一个方法, DataInterval应该Override从pair继承的setSecond方法.

DateInterval interval = new DateInterval(. . .);
Pair<LocalDate> pair = interval; // OK--assignment to superclass//pair的声明类型为Pair所以会调用public void setSecond(Object second)
//而无法调用 public void setSecond(LocalDate second)
pair.setSecond(aDate);

为了解决这个问题, 编译器会在DataInterval类自动生成一个bridge method

public void setSecond(Object second) { // 调用 public void setSecond(LocalDate second)setSecond((LocalDate) second);
}

当下面代码被调用时, pair.setSecond会调用bridge method, 然后bridge method调用实际的setSecond方法

DateInterval interval = new DateInterval(. . .);
Pair<LocalDate> pair = interval; // OK--assignment to superclass
pair.setSecond(aDate);

In summary, you need to remember these facts about translation of Java generics:

  • There are no generics in the virtual machine, only ordinary classes and
    methods.
  • All type parameters are replaced by their bounds.
  • Bridge methods are synthesized to preserve polymorphism.
  • Casts are inserted as necessary to preserve type safety

Restrictions and Limitations

Type Paramter Cannot be Instantiated with Primitive Types

  • there is no Pair<double>, only Pair<Double>.
  • 因为泛型擦除之后, Pair的类型是Object, double不是Object的子类

在运行时进行的类型查询只适用于原始类型(擦除之后的类型)

  • 对类型的查询只能使用Raw Type (擦除之后的类型)
    if (a instanceof Pair<String>) // Error
    if (a instanceof Pair<T>) // Error
//getClass method 返回的也是raw type
Pair<String> stringPair = . . .;
Pair<Employee> employeePair = . . .;
if (stringPair.getClass() == employeePair.getClass()) // they are equal

Java 不支持泛型数组

在Java中, 数组会记住元素的类型, 如果试图储存其他类型的元素, 就会抛出一个ArrayStoreException

  • 但是对于泛型类型, 擦除会使这种机制无效. 所以Java 不支持泛型数组
Pair<String>[] table = new Pair<String>[10];
Object[] objarray = table;
//数组中应该存入Pari<String>类型, 但是下面这行代码可以通过array store exception check因为擦除之后的类型是Object
//最终会在运行时报错,
objarray[0] = new Pair<Employee>();
  • 可以声明通配类型的数组, 然后进行类型转换, 但是结果将是不安全的
  • Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];
    If you store a Pair in table[0] and then call a String
    method on table[0].getFirst(), you get a ClassCastException.

不能实例化类型变量
You cannot use type variables in an expression such as new T(…).
For example, the following Pair constructor is illegal:

public Pair() { first = new T(); second = new T(); } // Error

Type Variable 不能用static关键字修饰

public class Singleton<T>
{private static T singleInstance; // Errorpublic static T getSingleInstance() // Error{if (singleInstance == null) construct new instance of Treturn singleInstance;}
}
  • 如果允许声明泛型类的static方法或者属性, 则一个程序可以声明两个类比如 Singleton<Random>, Singleton<JFileChooser>, 但是泛型擦除之后, 只有一个Singleton类和singleInstance属性. 则private static Random singleInstanceprivate static JFileChooser singleInstance 冲突, 所以不行

泛型类不能继承Throwable接口

public class Problem<T> extends Exception { /* . . . */ } // Error--can't extend Throwable

不能在catch中使用泛型

public static <T extends Throwable> void doWork(Class<T> t)
{try{do work}catch (T e) // Error--can't catch type variable{Logger.global.info(...)}
}

但是可以限定type variable继承Throwable接口

public static <T extends Throwable> void doWork(T t) throws T // OK
{try{do work}catch (Throwable realCause){t.initCause(realCause);throw t;}
}

注意擦除后的冲突

  • 比如将如下代码加入Pair class
public class Pair<T>
{public boolean equals(T value) { return first.equals(value) && second.equals(value); }. . .
}
  • 则会出现两个equals method, 解决方法是重新命名冲突的方法
    boolean equals(String) // defined in Pair<T>
    boolean equals(Object) // inherited from Object

泛型中的继承

  • 如果Manager是Employee的子类, 那么Pair<Manager>Pair<Employee>的子类吗? 答案是"NO"
  • 所以以下代码是错的, 不能把Pair<Manager> 赋给Pair<Employee>
Manager[] topHonchos = . . .;
Pair<Employee> result = ArrayAlg.minmax(topHonchos);

通配符类型 — Wildcard Types

类型通配符的分类

  • 类型通配符:<?> List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
    这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
  • 类型通配符上限:<? extends 类型>
    List<? extends Number>:它表示的类型是Number或者其子类型(也就是最大的就是Number类型)
  • 类型通配符下限:<? super 类型>
    List<? super Number>:它表示的类型是Number或者其父类型(也就是最小的就是Number类型)
public class GenericDemo {public static void main(String[] args) {//类型通配符:<?>List<?> list1 = new ArrayList<Object>();List<?> list2 = new ArrayList<Number>();List<?> list3 = new ArrayList<Integer>();System.out.println("--------");//类型通配符上限:<? extends 类型>//上线Number就是做大的,所以Object不行
//        List<? extends Number> list4 = new ArrayList<Object>();List<? extends Number> list5 = new ArrayList<Number>();List<? extends Number> list6 = new ArrayList<Integer>();System.out.println("--------");//类型通配符下限:<? super 类型>List<? super Number> list7 = new ArrayList<Object>();List<? super Number> list8 = new ArrayList<Number>();//下线就是Number最小的,所以其子类,Interger不行
//        List<? super Number> list9 = new ArrayList<Integer>();}
}

Java基础 --- 泛型 Generics相关推荐

  1. java基础-泛型举例详解

    泛型 泛型是JDK5.0增加的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数.这种类型参数可以在类.接口.和方法的创建中,分别被称为泛型类.泛型接口.泛型方法. 一.认识泛型 在没 ...

  2. java 基础 泛型

    泛型是什么? 泛型,即"参数化类型".一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参.那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类 ...

  3. Java基础----泛型

    一,泛型机制介绍及为何要使用泛型 泛型机制是在Java SE5.0中增加的,使用泛型机制编写的程序代码要比那些杂乱地使用object变量,然后再进行强制转换的代码具有更好的安全性和可读性.泛型对于集合 ...

  4. Java基础—泛型的使用(详细)

    文章目录 目录 零.概念 一.泛型的优点 1.1优点 1.2为什么要使用泛型 二.泛型使用与不使用的区别 1.泛型的没有使用会造成什么后果呢? 2.添加泛型的使用会发生什么效果呢? [一.二] 知识点 ...

  5. [Java基础]泛型基础

    可变参数的使用: 代码如下: package CanChangePack;import java.util.Arrays; import java.util.List;public class Arg ...

  6. 六、JAVA基础--泛型

    泛型方法:泛型方法可以放在普通类中,也可以定义在泛型类中. <T extends 具体类或者接口> T为绑定类型的子类型:T和绑定类型可以是类,也可以是接口. 可以有多个限定类型,用'&a ...

  7. java中generics的作用,java中泛型(Generics)的一些理解

    public class Point { // //    public Point(T x) { //        this.x = x; //    } private T x; private ...

  8. java基础之泛型(Generics)

    泛型,广泛存在于各种开源框架及容器集合类中,在我们阅读源码的过程中一定会碰到,作为java基础知识的一个重要模块,对泛型的理解和掌握有助于我们在之后的源码阅读中借鉴框架思想以及在项目开发中灵活应用泛型 ...

  9. Java基础学习——泛型(generics)学习一

    概述 在JDK 5.0,Java语言引入了好几个新的功能,其中很重要的一个就是泛型(generics). 本文就是对泛型的一个概述.你可以很熟悉其他语言中的类似结构,比如C++里的模板(templat ...

最新文章

  1. AM335x(TQ335x)学习笔记——使用dtb方式启动内核
  2. 一个转角---程序猿
  3. ElasticSearch7.x「新特性」
  4. 问题 C: 完美的数(思维)
  5. 米其林全球挑战赛电子门票欣赏
  6. python中numpy函数fft_如何在PyTorch中正确使用Numpy的FFT函数?
  7. 【漏洞预警】Intel爆CPU设计问题,导致win和Linux内核重设计(附测试poc)
  8. MinGw编译opencv教程
  9. 不同调制方式的包络和功率谱
  10. 搭建私有云=我使用的是开源的可道云,服务器是win service 2019+公网ip+动态域名解析
  11. Cdn英文的读音音标_教育 | 老司机请回答:孩子学英文自然拼读和中文拼音会不会混?...
  12. Google Chrome最强鼠标手势插件面世
  13. 【洛谷 P3191】 [HNOI2007]紧急疏散EVACUATE(二分答案,最大流)
  14. HTTPS中的数字证书是什么?数字签名又是什么?
  15. MAC 如何强制关闭程序
  16. 台式电脑 硬件选购 扫盲普及贴
  17. xcode mac app_IOS苹果APP签名详解
  18. 个人开发者可以申请微信支付了
  19. 【Unity Shader】(2)半兰伯特模型 构建光照
  20. 【数学有什么用处?看完后恍然大悟!】

热门文章

  1. CSS3,旋转rotate,动画animation
  2. matlab计算重心的函数,MATLAB计算出图像中多个物体的个数及重心.doc
  3. html滚动字幕循环,H5 div文字循环滚动
  4. 人脸检测、原理、技术和方法
  5. mongodb客户端编程
  6. Android截屏一键分享开发与实现方式的反思和总结
  7. DailyFi - 9.19|$PSTAKE 将对符合条件的用户进行空投
  8. 有没有更好的硬件设备售后管理痛点解决方案?
  9. 如何使用makedown(.md)做流程图
  10. python execjs库_python3调用js的库之execjs