泛型

泛型:对后续所有操作的类型做约束,对后续操作起作用,对之前的不起作用; 对类型进行约束; 

父 ----> 子,从范围上,父范围小,子范围大;把范围小的给范围大的,

JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了“类型形参”,这个类型形参将在声明变量、创建对象时确定,即传入实际的类型,我么称为“类型实参”。我们把这个“参数化的类型”称为泛型(Generic)。

我们可以为任何类和接口增加泛型声明,并不是只有集合类才可以使用泛型声明。

ArrayList<E>的<E>就是“类型形参”,ArrayList<String>的<String>就是“类型实参”,String类型就是用来确定E的类型用的。

为了区别,我们可以将int max(int a, int b)中a,b称为数据形参,将 int max = max(3,6);中3,6称为数据实参

泛型形参的命名一般使用单个的大写字母,如果有多个类型形参,那么使用逗号分隔,例如:Map<K,V>。

常见字母(见名知意):

例如定义学生类的成绩score不止一个类型:

...
public static void main(String[] args) { Student<Integer> stu = new Student(1, "kris", 99);
Student<String> stu1 = new Student(2, "smile", "优秀");
Student<Double> stu2 = new Student(5, "aa", 89.9);
}
} class Student<T>{ private int id;
private String name;
private T score;
.....
}

可以在定义接口、类时指定类型形参,类型形参在整个接口或类体中可以当成类型使用,几乎所有可使用其他普通类型的地方都可以使用这种类型形参,例如:属性类型、方法的形参类型、方法返回值类型等。

但是泛型类或泛型接口上的泛型形参,不能用于声明静态变量,也不能用在静态方法中,那是因为静态成员的初始化是随着类的初始化而初始化的,此时泛型实参无法指定,那么泛型形参的类型就不确定。

当创建带泛型声明的类时,为该类定义构造器时,构造器名还是原来的类名,不需要增加泛型声明。例如:Student<T>类定义的构造器依然是Student(),而不是Student<T>,但调用构造器时却可以使用Student<T>的形式,而且应该为T类型形参传入实际的类型实参。

泛型实参的要求

首先泛型实参必须是引用数据类型,不能是基本数据类型,可以指定为包装类型,因为集合中只能存储对象。

什么时候指定泛型实参?

(1)在用泛型类、接口声明变量时

(2)在继承泛型类或实现泛型接口时,如果子类不延续使用该泛型,那么必须明确指定实际类型。此时子类不再是泛型类了

(3)在创建泛型类对象时

public class EmployeeManager {

    private ArrayList<Employee> lis;
public static void test(ArrayList<String> lis){ ArrayList<String> list = new ArrayList<String>();
ArrayList<String> li = new ArrayList<>();//JDK1.7之后支持如下简化写法:
}
}
//继承泛型类
class MyArrayList extends ArrayList<String>{//此处ArrayList<String>就不能写成ArrayList<E>
//因为MyArrayList不再是泛型类了,因此E必须给出具体的类型
}
//实现泛型接口
class Employee implements Comparable<Employee>{ @Override
public int compareTo(Employee o) {
// TODO Auto-generated method stub
return 0;
} }

继承泛型类、实现泛型接口时,想要继续保留父类、父接口的泛型,必须在父类、父接口和子类、子接口中都要保留泛型。

设定泛型形参的上限

如果泛型形参没有设定上限,那么泛型实参可以是任意引用数据类型。如果泛型形参设定了上限(例如:T  extends  父类上限),那么只能指定为该父类本身或其各子类类型。

在一种更极端的情况下,程序需要为形参设定多个上限(至多有一个父类上限,可以有多个接口上限)表明该类型形参必须是其父类的子类(包括是父类本身也行),并且实现多个上限接口。

与类同时继承父类、实现接口类似的是:为类型形参指定多个上限时,所有的接口上限必须位于类上限之后。也就是说,如果需要为类型形参指定类上限,类上限必须位于第一位。

class Student<T extends Number & java.io.Serializable> {
//...省略其他代码
}
....
      Student<Integer> stu = new Student(1, "kris", 99);
//Student<String> stu1 = new Student(2, "smile", "优秀");//以下代码编译报错,因为String不是Number的子类
Student<Double> stu2 = new Student(5, "aa", 89.9); ArrayList<Integer> list = new ArrayList<>(); class Student<T extends Number>{ private int id;
private String name;
private T score;
public Student(int id, String name, T score) {
super();
this.id = id;
this.name = name;
this.score = score;
....
}

定义泛型方法

在定义类、接口时可以使用类型形参,在该类的方法和属性定义、接口的方法定义中,这些类型形参可被当成普通类型来用。但是,在另外一些情况下,(1)如果我们定义类、接口时没有使用类型形参,但是某个方法定义时,想要自己定义类型形参;(2)另外我们之前说类和接口上的类型形参是不能用于静态方法中,那么当某个静态方法想要定义类型形参。那么,JDK1.5之后,还提供了泛型方法的支持。

* 二、泛型方法
* 1、什么时候使用泛型方法
* (1)因为刚才说,泛型类或泛型接口上的泛型形参是不能用于静态成员的,
* 那么当静态方法需要用到泛型时,只能用泛型方法。
*
* (2)如果泛型类或泛型接口上的泛型形参,但是对于某个方法来说,不适用,或这个类或接口本身不是泛型类和泛型接口,
* 这个方法想要单独声明泛型,那么也得用方法方法,这个方法可以是非静态的
*
* 2、如何声明
* 【修饰符】 <泛型形参列表> 返回值类型 方法名(方法的形参列表) {
* }
* 泛型方法中声明的泛型形参,只能用在当前方法中。
*
* 需要实现这样的一个方法,该方法负责将一个数组的所有元素添加到一个Collection集合中
*
* 3、泛型方法的泛型形参是什么时候指定的
* 调用时
*
* 4、可以给泛型方法的泛型形参指定上限
* 【修饰符】 <泛型形参 extends 父类上限> 返回值类型 方法名(方法的形参列表) {
* }
*
* 需求:接收一个集合(里面都是图形),打印所有图形的面积
ArrayList<String> list = new ArrayList<String>();

..
public <T extends Graphic> void print(ArrayList<T> list){
      for (T t : graphics) {         System.out.println(t.getArea());     }   }
String[] array = {"Hello", "java"};
Collection<String> c = new ArrayList<String>();
//Object[] arr = array;//这样子是多态引用是可以的
//Collection<Object> coll = c;//但右边ArrayList<String>,左边是不接收的;报错
fromArrayToCollection(array, c);
//编译报错 public static void fromArrayToCollection(Object[]a, Collection<Object> c){
for(Object object : a){
c.add(object);
}
} //可以这样子写;//该方法负责将一个数组的所有元素添加到一个Collection集合中
public static <T> void copy(T[] arr, Collection<T> coll){
for (int i = 0; i < arr.length; i++) {
coll.add(arr[i]); }
}

泛型擦除

* 1、泛型的擦除:
* 当我们使用泛型类或泛型接口时,如果没有指定泛型的实参,那么它会出现泛型擦除的现象,
* 如果泛型形参有上限,就按照第一个上限处理,如果没有上限,就按照Object处理。
*
* 2、泛型类指定为不同泛型实参时,运行时是同一种类型
*
* 3、instanceof后面不支持泛型类;// 由于系统中并不会真正生成泛型类
*
* 4、泛型类不能创建数组; ArrayList<String>[] array = new ArrayList<String>[5]; //编译错误; this.arr = new T[length];
*
* 5、try..catch的catch中不能使用泛型
public class TestErase {

    public static void main(String[] args) {
ArrayList list = new ArrayList<String>();//ArrayList<String>被转换为了ArrayList
list.add("kris");
list.add("smile"); for (Object object : list) {
System.out.println(object);
} Object object = list.get(1); //泛型被擦除,按照默认上限Object处理 Student s = new Student("kk", 99); //Student<Number> Number score = s.getScore();//泛型被擦除,按照第一个上限Number处理 } } class Student<T extends Number>{
private String name;
private T score;
public Student(String name, T score) {
super();
this.name = name;
this.score = score;
}
public T getScore() {
return score;
} }
 public static void main(String[] args) {

        ArrayList<String> list1 = new ArrayList<>();
ArrayList<Integer> list2 = new ArrayList<>();
System.out.println(list1.getClass());//class java.util.ArrayList
System.out.println(list2.getClass());//class java.util.ArrayList
System.out.println(list1.getClass() == list2.getClass());//true ArrayList<Object> list = new ArrayList<String>(); //错误的 /*ArrayList<String>不是ArrayList<Object>的子类,
因为他们的运行时类型都是ArrayList,因此不允许如下赋值操作。
这点和数组不同,因为数组是要生成新的Class对象的,String[]仍然是Object[]的子类,因此允许如下赋值操作。
Object[] arr = new String[5];*/ }

通配符

* 通配符:?
* 1、类型通配符只能用于方法中,不能用在泛型类或泛型接口的声明上。
* 2、形式:
* (1) <?> 代表的是任意类型
* (2)<? extends 上限Upper> 代表的是Upper的类型或它的子类
* (3)<? super 下限Lower> 代表的是Lower的类或它的父类

当我们声明一个方法时,某个形参的类型是一个泛型类或泛型接口类型,但是在声明方法时,又不确定该泛型实际类型,我们可以考虑使用类型通配符。

                //使用类型通配符
public static void test(List<?> c){ //List c这种形式使用List接口时没有传入实际类型参数,这将引起泛型警告。
for (int i = 0; i < c.size(); i++) {//List<Object> c这种太局限了,调用时,只能传List<Object>,List<Object>不能接收List<String>等其他集合
System.out.println(c.get(i));
}
}
//声明一个泛型方法,需要声明泛型形参T。
public static <T> void test2(List<T> c){
for (int i = 0; i < c.size(); i++) {
System.out.println(c.get(i));
}
}

通配符的List仅表示它可以接受指定了任意泛型实参的List,并不能把元素加入其中,例如如下代码将会引起编译错误:

public static void test(List<?> c, String str){
c.add(str);
}

不知道上面程序中c集合里元素的类型,所以不能向其中添加对象,除了null对象,因为它是所有引用数据类型的实例。

test2方法带泛型的List,表示该集合的元素类型是T,因此允许T系列的对象加入其中,例如如下代码是可行的:

public static <T> void test(List<T> c, T t){
c.add(t);
}

如果不涉及添加元素到带泛型的集合中,那么两种方式都可以,如果涉及到添加元素到带泛型的集合中,使用类型通配符<?>的不支持。

当直接使用List<?>这种形式时,即表明这个List集合接收泛型实参指定为任意类型的List。

有时候我们只希望接收某些类型的List。

例如:一个图形的抽象父类Graphic,两个子类Circle和Rectangle。接下来我们想定义一个方法,可以打印不同图形的面积。

但是,List<Graphic>的形参只能接收List<Graphic>的实参,如果想要接收List<Circle>,List<Rectangle>的集合,可以使用List<?>。

但是这样有两个问题,一个是List<?>可以接收任意类型,不仅仅图形,第二个是需要强制类型转换。为了解决这个问题,Java允许设定通配符的上限:<? extends Type>,这个通配符表示它必须是Type本身,或是Type的子类。

public static void printArea(List<? extends Graphic> graphic){
for (Graphic g : graphic) {
System.out.println(g.getArea());
}
}
/*与前面的完全相同,因为我们不知道这个受限制的通配符的具体类型,所以我们不能把Graphic对象或其子类对象加入这个泛型集合中。
如果要需要将Graphic对象或其子类对象加入这个泛型集合,那么就只能用泛型方法了,*/ public static void printArea(List<? extends Graphic> graphics){
graphics.add(new Circle());//编译错误,因为不知道?的具体类型,也可能是Rectangle
}

返回的T是Object,也就是说,程序在复制集合元素的过程中,丢失了src集合元素的类型String。

对于上面的copy方法,可以这样理解两个集合参数之间的依赖关系:不管src集合元素的类型是什么,只要dest集合元素类型与src的元素类相同或是它的父类即可。为了表示这种约束关系,Java允许设定通配符的下限:<? super Type>,这个通配符表示它必须是Type本身或是Type的父类。

public static void main(String[] args) {
ArrayList<String> src = new ArrayList<String>();
src.add("kris"); //src -- >dest
ArrayList<Object> dest = new ArrayList<Object>(); String last = copy(dest, src);
System.out.println(last); } /* 实现将src集合里元素复制到dest集合中的功能,因为dest集合需要接受src的所有元素,
所以dest集合元素的类型应该是src集合元素的父类。
为了表示两个参数之间的类型依赖,考虑同时使用通配符、泛型形参类实现该方法。*/
/* public static <T> void copy (Collection<T> dest, Collection <? extends T> src){
for (T t : src) {
dest.add(t);
}
}*/
public static <T> T copy (Collection<? super T> dest, Collection <? extends T> src){
T last = null;
for (T t : src) {
dest.add(t);
}
return last;
}

JavaSE| 泛型的更多相关文章

  1. 「JavaSE 重新出发」05.02 泛型数组列表、包装类

    泛型数组列表 ArrayList 是一个采用类型参数(type parameter)的泛型类(generic class). java ArrayList<Employee> staff ...

  2. JavaSE学习笔记(9)---集合类和泛型

    JavaSE学习笔记(9)---集合类和泛型 1.Collection集合 集合概述 在前面我们已经学习过并使用过集合ArrayList<E> ,那么集合到底是什么呢? 集合:集合是jav ...

  3. [006] - JavaSE面试题(六):泛型

    第一期:Java面试 - 100题,梳理各大网站优秀面试题.大家可以跟着我一起来刷刷Java理论知识 [006] - JavaSE面试题(六):泛型 第1问:什么是泛型? Java泛型( generi ...

  4. JavaSE复习_8 泛型程序设计

    今晚看了core Java的泛型部分,万万没有想到,当时看培训班视频入门的一带而过的泛型,有这样多的细节,整理了一下书里面提到的一些自认为的重点,方便以后观阅.由于是复习,一些基础知识跳过. △泛型类 ...

  5. javaSE(九)之泛型(Generics)

    前言 这几天分享了怎么搭建集群,这一篇给大家介绍的是泛型,在我们的很多java底层的源代码都是有很多复杂的泛型的!那什么是泛型呢? 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是 ...

  6. JavaSE笔记-泛型

    定义带泛型的类 public class Cat<T> { //可以用T定义实例变量 private T name; //可以用T定义形参 //构造器没有<> public C ...

  7. JavaSE学习总结(十六)—— 泛型与泛型应用

    一.泛型概要 泛型(Generic)的本质是类型参数化,通俗的说就是用一个占位符来表示类型,这个类型可以是String,Integer等不确定的类型,表明可接受的类型. 泛型是Java中一个非常重要的 ...

  8. JavaSE习题 继承接口和泛型

    问答题: 1.子类在什么情况下可以继承父类友好成员? 答:在同一个包内 2.子类通过怎样的方法可以隐藏继承的成员变量? 答:声明一个与父类相同变量名的成员变量 3.子类重写继承的方法原则是什么? 答: ...

  9. javase高级技术 - 泛型

    在写案例之前,先简单回顾下泛型的知识 我们知道,java属于强变量语言,使用变量之前要定义,并且定义一个变量时必须要指明它的数据类型,什么样的数据类型赋给什么样的值. 所谓“泛型”,就是“宽泛的数据类 ...

随机推荐

  1. scp命令:远程复制粘贴文件

    文章链接:https://www.cnblogs.com/webnote/p/5877920.html scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有 ...

  2. windows连接服务端的域名正常,linux却不通,(针对于负载均衡后端节点设置)

    1.初步判断不是网络上的,因为windows主机访问正常, 2.修改客户端linux主机 net.ipv4.tcp_tw_recycle=0,测试是否正常,(服务器当连接数达到一定量之后,会执行rec ...

  3. 【进阶1-5期】JavaScript深入之4类常见内存泄漏及如何避免(转)

    这是我在公众号(高级前端进阶)看到的文章,现在做笔记 https://mp.weixin.qq.com/s/RZ8Lpkyk8lz6z5H8Q8SiEQ 垃圾回收算法 常用垃圾回收算法叫做**标记清除 ...

  4. C语言学习及应用笔记之三:C语言const关键字及其使用

    在C语言程序中,const关键字也是经常会用到的一个关键字,那么使用const关键字的目的是什么呢?事实上,在程序中使用const关键字的主要目的就是为了向使用者传递设计者的一些意图. 事实上,无论我 ...

  5. Android UiAutomator

    UiAutomator是一个做UI测试的自动化框架.<Android自动化测试框架>中已有详细介绍,这里就不再累赘了. 一.首先了解自动化测试流程 自动化需求分析 测试用例设计 自动化框架 ...

  6. GitHub搭配使用Travis CI 进行自动构建服务

    Travis CI (Continuous Integration)持续集成服务 用处:自动监控软件仓库,可以在代码提交后立刻执行自动测试或构建 1.在Github自己的仓库根目录里添加.travis ...

  7. LeetCode(82):删除排序链表中的重复元素 II

    Medium! 题目描述: 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字. 示例 1: 输入: 1->2->3->3->4->4- ...

  8. poj1185 状态压缩经典题

    状态压缩的好题,直接求会爆内存,先把所有可能的状态求出来存在stk里,然后f[i][k][t]表示i行状态为t,i-1状态为k,由i-1状态来推出i状态即可 注意要打好边际条件的状态,并且某个可行状态 ...

  9. linux学习笔记之 basename, dirname

    前言: basename: 用于打印目录或者文件的基本名称 dirname: 去除文件名中的非目录部分,仅显示与目录有关的内容.dirname命令读取指定路径名保留最后一个/及其后面的字符,删除其他部 ...

  10. 对象存储服务(Object Storage Service,简称 OSS)

    阿里云对象存储服务(Object Storage Service,简称 OSS),是阿里云提供的海量.安全.低成本.高可靠的云存储服务.它具有与平台无关的RESTful API接口,能够提供99.99 ...