1.泛型程序设计

泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用。例如:不希望为了聚集String和Integer对象分别设计不同的类。(个人觉得此处说的聚集译为:创建一个对象,属性可以为String和Integer类型。但是却有着相同的行为或属性)

代码如下:

public class StringTest {
private String age;
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
public class IntegerTest {
private Integer age;
public Integer getAge() {
return Age;
}
public void setAge(Integer age) {
this.age= age;
}
}

可以看到上面两个类都有age属性和方法。只是他们的属性类型有所不同。那么我们则可以使用到泛型:例

public class GenericTest<T> {
private T age;
public T getAge() {
return age;
}
public void setAge(T age) {
this.age= age;
}
}

然后只需要如下调用

    public static void main(String[] args){
GenericTest<String> genericString= new GenericTest<>();
GenericTest<Integer> genericInteger = new GenericTest<>();
}

这样我们就很好的解决了代码重用的问题,不是吗?看不懂?不急,我只是说了泛型的好处,让我们慢慢往下了解。

2.定义简单的泛型类

没接触过泛型的同学看到上面例子中的T可能会觉得一脸懵逼,其实T只是一个类的类型变量(虽然你可以定义A、B、C...,但是我们还是要规范点的是吧),然后T需要使用尖括号括起来,当你定义多个类型变量时。可以看下面的Generic类,

public class Generic<T,U> {
private T size;
private U length;
...
}

当你传入Generic<Integer,String> 时,你可以把这个类想象成如下使用

public class Generic {
private Integer size;
private String length;
...
}

所以,泛型类其实可以看做普通的工厂类。

3.泛型方法

上面介绍了一个简单的泛型类,让我们来看看泛型方法是怎么写吧。

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

可以看到,ArrayAlg是一个普通类,并不是一个泛型类。说明:泛型方法可以定义在普通类和泛型类中。

当我们调用getMiddle方法时,在方法名前的尖括号放入具体类型:

String s = ArrayAlg.<String>getMiddle("John","Q","Min");

看起来是不是很别扭?其实可以省略<String>类型参数,编译器会使用String[ ]数组与泛型T[ ]进行匹配推断出一定是String,这个应该就是他们私下bilibili的规则了。变成:

String s = ArrayAlg.getMiddle("John","Q","Min");

有的小伙伴可能会想到,酱紫的话,我能不能瞎比的传参数进去?例:

Double middle2 = ArrayAlg.getMiddle(3.14,0);

其实当你这样写的时候编译器就已经提示错误了,因为第二个参数编译器会默认认为是Integer类型。解决方法有两种:

1.把Double改成Number类型,因为Number类型为Double和Integer的父类。

2.自己改代码去吧,谁让你瞎搞。

4.类型变量的限定

有时候,类或方法需要对类型变量加以约束,假如,某个泛型方法需要使用到Comparable接口的compareTo方法,那我们则需要限制传进来的参数必须实现Comparable接口,例:

public static <T extends Comparable> T minmax(T[] a){
  compareTo...
}

传进来的参数a则必须实现Comparable接口。假如传进来参数需要实现Comparable和Serializable两个接口,则

public static <T extends Comparable&Serializable> Pair<T> minmax(T[] a){
compareTo...
}

叮咚!那么传进来的参数需要继承某个类怎么写?其实是一样的。传进来的参数需要继承某个类、实现某个接口都是使用extends关键字来控制。原因:选择关键字extends的原因是更接近子类的概念,并且Java的设计者也不打算在语言中再添加一个新的关键字

5.泛型代码和虚拟机

5.1类型擦除

Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类型在编译后都会被清除掉

例如:

public class GenericClass<T> {
private T first;
public T getFirst() {
return first;
}
...
}

擦除类型后

public class GenericClass {
private Object first;
public Object getFirst() {
return first;
}
...
}

可以看到,类型擦除后的GengericClass和没引用泛型没有什么两样。而且,当你使用GenericClass<String>后,也是变成原始的GenericClass类型。

但是当我们使用限定类型变量后,类型擦除后将会使用限定类型。例:

public class GenericClass<T extends Comparable> {
private T first;
...
}

可以看到限定了传进来的参数需要实现Comparable接口,那么类型擦除后如下:

public class GenericClass{
private Comparable first;
...
}

问题:当你限定了参数需要实现Comparable和Serializable接口,就是GenericClass<T extends Comparable&Serializable>时,类型擦除后会变成怎样呢?

public class GenericClass{
private Comparable first;
...
}

虚拟机会把首位实现的接口(此处为Comparable),转为擦除后对象类型。当然,如果你这样写GenericClass<T extends Serializable&Comparable> ,那么擦除后的类型为Serializable。

5.2翻译泛型表达式

当程序调用泛型方法时,如果擦除返回类型, 编译器将插入强制类型转换。例如List集合源码:

public interface List<E> extends Collection<E> {
...
E get(int index);
...
}

当不使用类型变量时,取出来的值是Object,而定义了String类型变量时,编译器将插入强制类型转换:

List list1 = new ArrayList();
list1.add("1");
list1.get(0); //Object List<String> list2 = new ArrayList<>();
list2.add("1");
list2.get(0); //String

但是在虚拟机中时,list2.get(0)在调用时翻译成了两条虚拟机命令:

1.对原始方法List集合的get方法调用

2.将返回的Object进行强制类型转换成String

5.3翻译泛型方法

当GenericSubClass类继承了Generic类时,泛型方法会有什么变化?

public class Generic<T> {
private T size;
public T getSize() {
return size;
}
...
}
public class GenericSubclass extends Generic<Integer> {
@Override
public Integer getSize() {
return super.getSize();
}
}

此时的@Override真的是重写父类方法吗?到了运行期间,Generic被类型擦除后getSize方法返回类型变成Object:

public Object getSize() { return size; }

而GenericSubclass的getSize方法的返回类型却是Integer:

public Integer getSize() { return super.getSize(); }

这并不是重写父类的方法,因为方法并不一样,所以导致GenericSubclass在虚拟机中却有了两个getSize方法:

public Integer getSize() {  ...  }
public Object getSize() { ... }

导致的原因就是类型擦除与多态发生了冲突。要解决此问题,虚拟机自动在GenericSubclass中生成了一个桥方法:

public Integer getSize() { return (Integer)super.getSize(); }

最后,虚拟机就会调用自己生成的桥方法来解决此冲突。

若有不足之处,请各位大牛在下面留言指出,Thanks♪(・ω・)ノ

 

Java核心技术第八章——泛型程序设计(1)的更多相关文章

  1. Java核心技术第八章-泛型

    摘要 本文根据<Java核心技术 卷一>一书的第八章总结而成,部分文章摘抄书内,作为个人笔记. 文章不会过于深入,望读者参考便好. 为什么要使用泛型程序设计 泛型程序设计(Generic ...

  2. Java核心技术点之泛型

    1. Why ——引入泛型机制的原因 假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过了一阵,我们想要实现一个大小 ...

  3. java核心技术学习笔记之一程序设计概述

    Java 核心技术之一程序设计概述 一.   Java语言的特点 简单行 :取经于C++,排除了C++不常用的指针.结构等,增加垃圾回收. 面向对象:与C++不同是单继承,但是可以继承多接口.完全面向 ...

  4. java核心卷轴之泛型程序设计

    本文根据<Java核心卷轴>第十二章总结而来,更加详细的内容请查看<Java核心卷轴> 1. 泛型类型只能是引用类型,不可以使用基本数据类型. 2. 类型变量含义 E : 集合 ...

  5. java核心技术学习笔记之一程序设计环境

    一术语 JDK:Java Delelpment Jit JRE:Java Runtime Environment 二.安装jdk1.8.0_25 设置环境变量(建议直接安装在C盘下),使用:隔开 C: ...

  6. java核心技术学习笔记之三程序设计结构

    一 基本数据结构 必须包括在类中 必须具备 public static main方法 大小写敏感 二.数据类型 四种整数类型: Int 4字节 short 2字节 long8字节 byte1字节 二种 ...

  7. Java核心技术卷一基础知识-第12章-泛型程序设计-读书笔记

    第12章 泛型程序设计 本章内容: * 为什么要使用泛型程序设计 * 定义简单泛型类 * 泛型方法 * 类型变量的限定 * 泛型代码和虚拟机 * 约束与局限性 * 泛型类型的继承规则 * 通配符类型 ...

  8. Java核心技术-泛型程序设计

    使用泛型机制编写的代码要比那些杂乱地使用Object变量,然后再进行强制类型转换的代码具有更好的安全性和可读性. 泛型对于集合类尤其有用 1 为什么要使用泛型程序设计 泛型程序设计意味着编写的代码可以 ...

  9. [Effective Java]第八章 通用程序设计

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

随机推荐

  1. Kali Linux安装中文输入法全纪录

    前言: 我使用的是英文版的Kali,默认没有安装中文输入法,也没有安装小企鹅(我后来才知道),折腾了很久,现在终于可以在Kali里输入中文了(这篇文章就是在Kali里面用leafpad写的).安装的过 ...

  2. Elasticsearch: 权威指南 » 深入搜索 » 多字段搜索 » 多数字段 good

      跨字段实体搜索  » 多数字段编辑 全文搜索被称作是 召回率(Recall) 与 精确率(Precision) 的战场: 召回率 ——返回所有的相关文档:精确率 ——不返回无关文档.目的是在结果的 ...

  3. Java开源生鲜电商平台-售后模块的设计与架构(源码可下载)

    Java开源生鲜电商平台-售后模块的设计与架构(源码可下载) 说明:任何一个的电商平台都有售后服务系统,那么对于我们这个生鲜的电商平台,售后系统需要思考以下几个维度. 1. 买家的需求维度 说明:买家 ...

  4. Nginx 入门学习教程

    昨天听一个前同事说他们公司老大让他去研究下关于Nginx 方面的知识,我想了下Nginx 在如今的开发技术栈中应该会很大可能会用到,所以写篇博文记录总结下官网学习教程吧. 1. 什么是Nginx? 我 ...

  5. BZOJ_1552_[Cerc2007]robotic sort_splay

    BZOJ_1552_[Cerc2007]robotic sort_splay 题意: 分析: splay维护区间操作 可以先把编号排序,给每个编号分配一个固定的点,映射过去 查找编号的排名时先找到这个 ...

  6. laravel 中路由的快速设置(只需一个控制器名就ok) 不用具体到方法

    routes/web.php 设置路由 Route::group(['middleware' => ['\iqiyi\Http\Middleware\VerifyCsrfToken::class ...

  7. 显著性检测(saliency detection)评价指标之NSS的Matlab代码实现

    calcNSSscore.m function [ score ] = calcNSSscore( salMap, eyeMap ) %calcNSSscore Calculate NSS score ...

  8. 记一次springboot项目,maven引发的悲剧(Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletCont)

    maven下载大的项目的时候,jar包下载出错是常见的, 但是这种情况经常能看到,如java.lang.ClassNotFoundException这样的提示, 所以一直以来也觉得maven下载jar ...

  9. typeof和instansof的区别

    typeof typeof 是一个一元运算,放在一个运算数之前,运算数可以是任意类型. 它返回值是一个字符串,该字符串说明运算数的类型.(typeof 运算符返回一个用来表示表达式的数据类型的字符串. ...

  10. React 中阻止事件冒泡的问题

    在正式开始前,先来看看 JS 中事件的触发与事件处理器的执行. JS 中事件的监听与处理 事件捕获与冒泡 DOM 事件会先后经历 捕获 与 冒泡 两个阶段.捕获即事件沿着 DOM 树由上往下传递,到达 ...