Java:泛型小记

对 Java 中的 泛型类,做一个微不足道的小小小小记

泛型实现

概述

开篇:

List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass()); // 存在类型擦除:true

泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。

例如:List<String> 在运行时仅用一个 List 来表示。这样做的目的,是确保能和 Java 5 之前的版本开发二进制类库进行兼容。

类型擦除:

泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。

在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 <T> 则会被转译成普通的 Object 类型,如果指定了上限如 <T extends String> 则类型参数就被替换成类型上限。

泛型中值得注意的地方:

  1. 泛型类或者泛型方法中,不接受 8 种基本数据类型

    // 无法通过编译
    List<int> li1 = new ArrayList<>();
    List<boolean> li2 = new ArrayList<>();
    // 需要转换成其包装类
    List<Integer> li1 = new ArrayList<>();
    List<Boolean> li2 = new ArrayList<>();
  2. Java 不能创建具体类型的泛型数组

    // 无法通过编译
    List<Integer>[] li3 = new ArrayList<Integer>[];
    List<Boolean>[] li4 = new ArrayList<Boolean>[];

    原因还是类型擦除带来的影响,List<Integer>List<Boolean>在 JVM 中等同于List<Object>, 所有的类型信息都被擦除, 程序也无法分辨一个数组中的元素类型具体是 List<Integer>类型还是 List<Boolean>类型。

补充:

List<String> list = new ArrayList<String>();

1、两个 String 其实只有第一个起作用,后面一个没什么卵用,只不过 JDK7 才开始支持 List<String>list = new ArrayList<> 这种写法。

2、第一个 String 就是告诉编译器,List 中存储的是 String 对象,也就是起类型检查的作用,之后编译器会擦除泛型占位符,以保证兼容以前的代码。

通配符

通配符 :除了用 <T>表示泛型外,还有 <?>这种形式。? 被称为通配符

为何需要引入通配符 ?的概念:

class Base{}
class Sub extends Base{} // 以下代码无问题:
Sub sub = new Sub();
Base base = sub;
List<Sub> lsub = new ArrayList<>();
// error! 无法编译通过
List<Base> lbase = lsub;

编译器不会让它通过的。Sub 是 Base 的子类,不代表 List<Sub>List<Base>有继承关系。

但是,在现实编码中,确实有这样的需求,希望泛型能够处理某一范围内的数据类型,比如某个类和它的子类,对此 Java 引入了通配符这个概念。

因此,通配符的出现是为了指定泛型中的类型范围

通配符的三种形式:

  1. <?>被称作无限定的通配符。
  2. <? extends T>被称作有上限的通配符。
  3. <? super T>被称作有下限的通配符。

无限定的通配符它其中的 ? 其实代表的是未知类型,所以涉及到 ? 时的操作,一定与具体类型无关;

有上限的通配符<? extends T>

确保类型必须是 T 的子类来设定类型的上界,例如 List<? extends Number> 可以接受 List<Integer>List<Float>

有下限的通配符<? super T>它通过确保类型必须是 T 的父类来设定类型的下界

// 定义类
class Person{
private String name; public Person() {
} public Person(String name) {
this.name = name;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
} class Student extends Person{
public Student() {
} public Student(String name) {
super(name);
}
} class Animal{
private String name; public Animal(String name) {
this.name = name;
} @Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
} @Test
public void test06(){
ArrayList<Person> persons = new ArrayList<>();
persons.add(new Person("张三"));
persons.add(new Person("李四"));
persons.add(new Person("王五"));
printOnlyPerson(persons);
printPerson(persons);
printWho(persons); ArrayList<Student> students = new ArrayList<Student>();
students.add(new Student("小红"));
students.add(new Student("小明"));
students.add(new Student("小丁"));
// printOnlyPerson(students); // error
printPerson(persons);
printWho(persons); ArrayList<Animal> animal = new ArrayList<Animal>();
animal.add(new Animal("小猫"));
animal.add(new Animal("小狗"));
animal.add(new Animal("小鸟"));
// printOnlyPerson(animal); // error
// printPerson(animal); // error
printWho(animal);
} // 只能打印限定Person类别
public void printOnlyPerson(ArrayList<Person> persons){
Iterator<Person> iterator = persons.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next().toString());
}
} // 可以打印Person类及其子类
public void printPerson(ArrayList<? extends Person> persons){
Iterator<? extends Person> iterator = persons.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next().toString());
}
} // 可以打印任何东西
public void printWho(ArrayList<?> who){
Iterator<?> iterator = who.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next().toString());
}
}

补充:Array 不支持泛型,要用 List 代替 Array,因为 List 可以提供编译器的类型安全保证,而 Array 却不能。

参考

https://blog.csdn.net/briblue/article/details/76736356

Java:泛型小记的更多相关文章

  1. Java泛型小记

    Automobile类: public class Automobile { private String name; public Automobile(String name){ this.nam ...

  2. Java泛型的历史

    为什么Java泛型会有当前的缺陷? 之前的章节里已经说明了Java泛型擦除会导致的问题,C++和C#的泛型都是在运行时存在的,难道Java天然不支持“真正的泛型”吗? 事实上,在Java1.5在200 ...

  3. 浅析Java 泛型

    泛型是JavaSE5引入的一个新概念,但是这个概念在编程语言中却是很普遍的一个概念.下面,根据以下内容,我们总结下在Java中使用泛型. 泛型使用的意义 什么是泛型 泛型类 泛型方法 泛型接口 泛型擦 ...

  4. Java:泛型基础

    泛型 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚很多! 解决这种限制的 ...

  5. java泛型基础

    泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法.  Ja ...

  6. 使用java泛型设计通用方法

    泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张stu ...

  7. 关于Java泛型的使用

    在目前我遇到的java项目中,泛型应用的最多的就属集合了.当要从数据库取出多个对象或者说是多条记录时,往往都要使用集合,那么为什么这么使用,或者使用时有什么要注意的地方,请关注以下内容. 感谢Wind ...

  8. 初识java泛型

    1 协变数组类型(covariant array type) 数组的协变性: if A IS-A B then A[] IS-A B[] 也就是说,java中的数组兼容,一个类型的数组兼容他的子类类型 ...

  9. 【Java心得总结四】Java泛型下——万恶的擦除

    一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...

随机推荐

  1. redis 主从复制详解

    引言 我们之前操作 Redis 都是单机版,但是实际应用中没人使用单机版,都是搭建集群的方式.这篇文章要介绍的主从复制,是指将一台 Redis 服务器的数据,复制到其他 Redis 服务器,我们将前者 ...

  2. squid缓存代理

    目录: 一.Squid 代理服务器 二.Squid 代理安装 三.搭建传统代理 四.搭建透明代理 五.ACL访问控制 六.Squid日志分析 七.反向代理 一.Squid 代理服务器Squid 主要提 ...

  3. SpringBoot自定义初始化Bean+HashMap优化策略模式实践

    策略模式:定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户. 传统的策略模式一般是创建公共接口.定义公共方法-->然后创建实体类实现公共接口.根据各自的 ...

  4. 2.1Java基础

    2.1.9面向对象的三大特性(携程): 封装:把一个对象的属性隐藏在对象内部,外部对象不能直接访问这个对象的内部信息.但是可以提供一些可以被外界访问的方法来操作属性.就比如我们常常创建一个类,把他的属 ...

  5. 洛谷P1208——P1208 [USACO1.3]Mixing Milk(贪心)

    题目描述 由于乳制品产业利润很低,所以降低原材料(牛奶)价格就变得十分重要.帮助Marry乳业找到最优的牛奶采购方案. Marry乳业从一些奶农手中采购牛奶,并且每一位奶农为乳制品加工企业提供的价格是 ...

  6. HCNP Routing&Switching之路由控制、路由策略和IP-Prefix List

    前文我们了解了IS-IS路由聚合和认证相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15306645.html:今天我们来聊一聊路由控制技术中的路由策 ...

  7. tp5 引入 没有命名空间的类库的方法(以微信支付SDK为例)

    use think\Loader; Loader::import('Wxpay.WxPay',EXTEND_PATH,'.Api.php'); 注意扩展名的点"."不能省略 使用之 ...

  8. Go学习【01】:初步学习需要的知识

    理解以下知识,初步写简单go项目就足够了 语言参考(基础篇) 基本语法 基本组成 包声明 引入包 函数 变量 语句 & 表达式 注释 其它(可忽略) go没有像php.js那样需要在文件开始申 ...

  9. python BeautifulSoup html解析

    * BeautifulSoup 的.find(), .findAll() 函数原型 findAll(tag, attributes, recursive, text, limit, keywords) ...

  10. shell 脚本获取数组字符串长度

    #!/bin/sh source /etc/init.d/functions funOne() { array=(I am dfh kjlhfjksdf sdfj jdkfhaskl mjjoldfu ...