<T>泛型,广泛的类型
其实早在1999年的JSR 14规范中就提到了泛型概念,知道jdk5泛型的使用才正式发布,在jdk7后,又对泛型做了优化,泛型的推断.
泛型类
public class Pair<T> {
    private T first;
    private T second;
    public Pair() {
        first = null;
        second = null;
    }
    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }
    public void setFirst(T newValue) {
        first = newValue;
    }
    public void setSecond(T newValue) {
        second = newValue;
    }
}
怎么理解泛型类的定义.首先在类名后根上<T>这个T就是任意类型.在 Java 库中, 使用变量 E 表示集合的元素类型, K 和 V 分别表示表的关键字与值的类型。T ( 需要时还可以用临近的字母 U 和 S) 表示“ 任意类型”。然后在类中的成员,都可以使用这个T,你既可以把T当做参数,也可以把T当做返回值.也可以把T当做成员变量的类型.这个T到底存储的什么类型,取决于你在实例化Pair时指定的具体类型.但是以上写法,你一旦指定了一个实际类型,那么这个类中所有的T都会是同一个类型.
public class Main {
    public static void main(String[] args) {
        Pair<String> pair = new Pair<>();
        pair.setFirst("第一");
        pair.setSecond("第二");
    }
}
你也可以在一个泛型类上定义多个泛型
public class Pair<T,U> {
    private T first;
    private U second;
    public Pair() {
        first = null;
        second = null;
    }
    public Pair(T first, U second) {
        this.second = second;
        this.first = first;
    }
    public void setFirst(T newValue) {
        first = newValue;
    }
    public void setSecond(U newValue) {
        second = newValue;
    }
}
但是你需要记得,因为泛型的作用域在类级别.一下写法是错误的.

你在类上定义了个T表示,你所实例化的每一个类都要指定一个类型,现在,现在你试图不做类的实例化,而直接使用T,那么这个T你要从哪里定义呢?记住要使用泛型,先确定泛型的具体类型.
泛型方法
public class Demo3 {
    public   <T> void show(T t){
        System.out.println(t.toString());
    }
    public static  <S> void show2(S s){
        System.out.println(s);
    }
public class Main {
    public static void main(String[] args) {
        Demo3 demo3 = new Demo3();
        demo3.<String>show("a");
        demo3.show("a");
        Demo3.show2(1);
    }
}
你在一个方法上指定了泛型,即泛型的作用域在方法体上,也就是说,你每次调用方法都要指定具体的类型.当然你不用每次调用都使用<T>语法,因为jdk7的泛型推断.编译器自然可以通过你的实参而推断出你想要的实际类型.将泛型定义到方法上,泛型的T可以用到参数,方法体,返回值.
当然在你指定泛型时,也可以有以下写法
public class Demo1 {
    public  <String> void add(String t){
    }
}
不过这通常是没有任何意义的,否则,你要想表达什么呢?定义了一个泛型方法,并且限定泛型的实际类型是String?
泛型的擦除
泛型的擦除可谓是泛型中的重中之重了.字面意思,泛型类型会被擦除.引起两个问题:1在什么情况下擦除2被擦除后的的类什么样.
文档说明泛型只在编译器用来检测类型,编译时即会擦除泛型.所以泛型是在编译时被擦除的.下面看一下代码
public class Demo4<T> {
    private T type;
    public void add(T t) {
    }
}

可以看出在无限定类型时(没有使用extends 或 super 限定泛型)在编译后原来的T被替换成了Object.
泛型表达式
看以下代码
public class Pair<T> {
    private T first;
    private T second;
    public Pair() {
        first = null;
        second = null;
    }
    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }
    public T getFirst() {
        return first;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public T getSecond() {
        return second;
    }
    public void setSecond(T second) {
        this.second = second;
    }
}
public class Main {
    public static void main(String[] args) {
        Pair<Student> pair = new Pair<>();
        Student s = pair.getFirst();
    }
}
前边已经说过,对于无限定类型,在编译时会擦掉泛型的而变成object.那么以上这个Main中运行的代码,pair通过get()方法的返回值确可以直接赋值给Student这又是怎么回事呢?


这是两个class反编译后的.可以看到,在get()方法后,编译器帮我们自动做了类型转换
泛型与多态的冲突(桥方法)
public class Pair<T> {
    private T first;
    private T second;
    public Pair() {
        first = null;
        second = null;
    }
    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }
    public T getFirst() {
        return first;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public T getSecond() {
        return second;
    }
    public void setSecond(T second) {
        this.second = second;
    }
}
public class PairChild extends Pair<Person> {
    @Override
    public void setFirst(Person first) {
        super.setFirst(first);
    }
}
我们继承了Pair并且指定了他的具体类型.那么我们覆盖Pair方法时就只能传入Person类型的参数了.那么,在已经编译好的Pair.class中,setFirst()应该还是Object类型.这时我们到底算是覆盖父类的方法了吗?
Pair.class

PairChild.class

其中object参数的方法就是桥方法,当我们使用setFirst时,会先调用这个桥方法,这个桥方法,会将object强制转换成Person然后在调用PairChild自己的setFirst().
打破泛型的约束
有时间泛型对待数据类型也不是绝对安全的.请看一下示例
public class Pair<T> {
    private T first;
    private T second;
    public Pair() {
        first = null;
        second = null;
    }
    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }
    public T getFirst() {
        return first;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public T getSecond() {
        return second;
    }
    public void setSecond(T second) {
        this.second = second;
    }
}
public class PairSort  {
    public void sort(Pair pair){
        pair.setFirst("可是我是字符串");
    }
}
public class Main {
    public static void main(String[] args) {
        PairSort pairSort = new PairSort();
        Pair<Person> pair = new Pair();
        pairSort.sort(pair);
        Person first = pair.getFirst();
    }
}
我们有一个PairSort类,这个类接受一个Pair实例,但是并没有指定泛型,也就是说,它现在接受的是一个Object.然后给他的却是一个指定了Person类型的Pair.这时我们在调用Pair就获得了一个错误.

约束与限制
1 不能使用基本类型实例化类型参数,我们不能传递基本数据类型当做泛型的具体类型,原因是当泛型擦除后需要转换成具体的object子类.而基本数据类型,并不能转换成object
2 检查一个对象的类型不能带泛型参数.看一下代码

3 不能创建参数化类型的数组

4 不能实例化类型变量

5 尽管有泛型的擦除,但是在静态中依然不能使用泛型

6 泛型类中不能覆盖父类的方法
通配符
请看以下错误代码
public class Pair<T> {
    private T first;
    private T second;
    public Pair() {
        first = null;
        second = null;
    }
    public Pair(T first, T second) {
        this.second = second;
        this.first = first;
    }
    public T getFirst() {
        return first;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public T getSecond() {
        return second;
    }
    public void setSecond(T second) {
        this.second = second;
    }
}
public class Employee {
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public class Manager extends Employee {
}
public class Main {
    public static void main(String[] args) {
        Pair<Manager> pair = new Pair<>();
        printBuddies(pair);//错误的
    }
    public static void printBuddies(Pair<Employee> p) {
        Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(first.getName());
    }
}
printBuddies()需要一个Pair类型参数,我们指定Pair的泛型类型是Employee.当我们传递实参时,传递的是Pair<Manager>类型的实参,这是不正确的.Pair<Employee>和Pair<Manager>没有父子关系.
他们都只是Pair类型.那么对于这种问题有没有解决方案的?
public class Main {
    public static void main(String[] args) {
        Pair<Manager> pair = new Pair<>();
        printBuddies(pair);
        Pair<Employee> employeePair = new Pair<>();
        printBuddies(employeePair);
    }
    public static void printBuddies(Pair<? extends Employee> p) {
        Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(first.getName());
    }
}
使用 ? extends Employee 指定Pair的泛型类型是Employee或者是其子类.这样就不会出现编译异常
看一下对通配符的使用
public class Main {
    public static void main(String[] args) {
        Pair<Manager> pair  = new Pair<>();
        Pair<? extends Employee> pair1 = pair;
        pair.setFirst(new Employee());//错误的
        pair.setFirst(new Manager());
        Employee first = pair.getFirst();
    }
}
我们创建一个Manager类型的Pair.将他赋值给Pair<Employee>这是没错的.但是当我们调用set方法则会出现编译异常.原因是编译器知道我们要传入Employee的子类型但是不知道具体传入的是哪个子类型所以
拒绝编译.但是get方法则没有问题,因为返回是一个Employee
超类通配符
public class Main {
    public static void main(String[] args) {
        printBuddies(new Pair<Employee>());
        printBuddies(new Pair<Manager>());
    }
    public static void printBuddies(Pair<? super Manager> p) {
        Object first = p.getFirst();
        Object second = p.getSecond();
    }
}
我们限定printBuddies的参数为Manager或者其父类.

由于我们希望传入Manager或者其父类所以get方法拒绝我们用一个Manager接收.它无法确定我们到底传入的是Manager还是其父类所以只能用Object接收.
无限定通配符

虽是无限定通配符,但是他的使用限定是最大的.我们甚至无法使用set方法.除非传递一个null.Object都不行.在get方法时我们也只能用Object来接收.那么为什么要有这样一个鸡肋的通配符呢?

<T>泛型,广泛的类型的更多相关文章
- 泛型T的类型获取
		
T.getClass()或者T.class都是非法的,因为T是泛型变量. 由于一个类的类型是什么是在编译期处理的,故不能在运行时直接在Base里得到T的实际类型. /** * 可以在service层直 ...
 - 使用C#反射中的MakeGenericType函数,来为泛型方法和泛型类指定(泛型的)类型
		
C#反射中的MakeGenericType函数可以用来指定泛型方法和泛型类的具体类型,方法如下面代码所示这里就不多讲了,详情看下面代码一切就清楚了: using System; using Syste ...
 - Java泛型-内部原理: 类型擦除以及类型擦除带来的问题
		
一:Java泛型的实现方法:类型擦除 大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除.Java的泛型基本上都是在编译 ...
 - C# 泛型多种参数类型与多重约束 示例
		
C# 泛型多种参数类型与多重约束 示例 interface IMyInterface { } class Dictionary<TKey, TVal> where TKey : IComp ...
 - Gson通过借助TypeToken获取泛型参数的类型的方法
		
最近在使用Google的Gson包进行Json和Java对象之间的转化,对于包含泛型的类的序列化和反序列化Gson也提供了很好的支持,感觉有点意思,就花时间研究了一下. 由于Java泛型的实现机制,使 ...
 - Java进阶(四)Java反射TypeToken解决泛型运行时类型擦除问题
		
在开发时,遇到了下面这条语句,不懂,然习之. private List<MyZhuiHaoDetailModel> listLottery = new ArrayList<MyZhu ...
 - Java 8新特性探究(三)泛型的目标类型推断
		
简单理解泛型 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.通俗点将就是"类型的变量".这种类型变量可以用在类.接口和方法 ...
 - 如何在运行时(Runtime)获得泛型的真正类型
		
前言 由于Java 的类型擦除机制,在编译时泛型都被转为了Object,例如List<String>经过编译之后将变为类型 List.可以通过以下的方式再运行时获得泛型的真正类型 泛型如何 ...
 - Gson通过借助TypeToken获取泛型参数的类型的方法(转)
		
最近在使用Google的Gson包进行Json和Java对象之间的转化,对于包含泛型的类的序列化和反序列化Gson也提供了很好的支持,感觉有点意思,就花时间研究了一下. 由于Java泛型的实现机制,使 ...
 - java 泛型没有协变类型, 所以要重用extends, 但使用List<? extends Fruit> 可以是ArrayList<Fruit>()、ArrayList<Apple>()、ArrayList<Orange>(), 因此不能add元素进去
		
class Fruit{} class Apple extends Fruit{} class SubApple extends Apple{} class Orange extends Fruit{ ...
 
随机推荐
- C# 使用Environment获取当前程序运行环境相关信息
			
Enviroment类和AppDomain类前者表示系统级的相关信息,后者表示应用程序级的相关信息. 我常用这两个类获取一些程序运行目录.操作系统位数等信息: string basedir = App ...
 - JS读取xml
			
xml文件 <?xml version="1.0" encoding="utf-8"?> <root> <data id=&quo ...
 - charles注册码及中文版本,支持window和mac
			
安装证书: 安装完证书之后设置代理 2个* ,代表全部 注册码: Registered Name: https://zhile.io License Key: 48891cf209c6d32bf4 破 ...
 - c++和java的一些debug方法
			
就上面那个绿色的小瓢虫,点了就进了debug模式. 好尴尬啊,就说一句话. 而且,要加断点,不然就一下debug完了.
 - java 学习 进阶之 一 (线程基础)
			
一.线程安全 线程安全的概念:当多个线程访问某一个类(对象或方法)时.这个类始终都能表现出正确的行为那么这个类(对象或方法)就是线程安全的. synchronized:可以在任何对象及方法上加锁,而加 ...
 - js 设计模式——策略模式
			
策略模式(Strategy) 定义:将定义的一组算法封装起来,使其相互之间可以替换.封装的算法具有一定的独立性,不会随客户端的变化而变化 废话不多说,先来个例子 // 例如要写一个计算两个数加减乘除的 ...
 - Python 报错 MySQLdb._exceptions.OperationalError: (2059, )
			
Python连接MySQL数据时:报错提示MySQLdb._exceptions.OperationalError: (2059, <NULL>). Python包: mysqlclien ...
 - 【MongoDB详细使用教程】三、高级查询
			
目录 1.使用比较运算符查询 2.使用关键字查询 2.1.in/not in 关键字 2.2.size 关键字 2.3.exists 关键字 2.4.or 关键字 3.模糊查询 4.查询结果排序 5. ...
 - 使用 PXE+Kickstart 实现无人值守批量部署系统
			
一.废话两句 在云数据中心,一次几十台甚至几百台服务器上线,系统安装将变得非常繁琐,系统安装好了后还会涉及很多配置,如果一台台来安装的话工作量非常大.(虽然有加班费,开个玩笑)为了解决这个问题,我们需 ...
 - 关于重学Linux的随笔
			
毕业已有半年, 现在想想真的后悔, 大学没有认真学Linux, 导致现在Linux操作抓瞎, 连服务器都搭不起来. 下定决心重学Linux, 不追求能比上大佬, 但是要熟练, 常用命令要熟悉. 作为一 ...