18.Java泛型

news2024/12/23 16:39:47

目录


1. Java基本介绍

2. JDK下载安装及其环境配置

3. 一个简单的java程序

4. Eclipse基本使用、数据类型、运算符

5. 控制语句(if、switch、for、while、foreach)

6. Java数组

7. Java字符串对象(String|StringBuffer|StringBuilder|StringJoiner)

8. Java面向对象

9. 正则表达式

10. Java包装类

11. 日期与时间

12. 异常(throwable)

13. Java日志处理

14. Java集合框架(Collection)

15. Java多线程

16. Java IO数据流

17. Java枚举(Enum)

18. Java泛型

19. Java Class类和Java反射

20. JDBC操作数据库

21. Java网络通信

22. Java注解

23. Java 1.8新特性

24. Java函数式编程




泛型

泛型就是定义一种模板,既实现了编写一次,万能匹配,又通过编译器保证了类型安全:这就是泛型

泛型:编写模板代码来适应任意类型
(1)泛型的好处是使用时不必对类型进行强制转换,它通过编译器对类型进行检查
(2)使用泛型时,把泛型参数<T>替换为需要的class类型。ArrayList<String>、ArrayList<Number>等
注意泛型继承关系:可以把ArrayList<Integer>向上转型为List(T不变),但不能把ArrayList<Integer>向上转型为ArrayList<Number>(T不能变成父类)
可以省略编译器能自动推断出类型,例如:List<String> list = new ArrayList<>();(T省略)

(3)不指定泛型参数类型时,编译器会给出警告,且只能将视为Object类型
(4)可以在接口中定义泛型类型,实现此接口的类必须实现正确的泛型类型

(1)类型安全
泛型的主要目标是实现Java的类型安全。泛型可以使编译器知道一个对象的限定类型是什么,这样编译器就可以在一个高的程度上验证这个类型
(2)消除了强制类型转换,使得代码可读性好,减少了很多出错的机会

Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率

泛型实质
泛型实质:就是使程序员定义安全的类型
在没有出现泛型之前,Java也提供了对Object的引用“任意化”操作,这种任意化操作就是对Object引用进行“向下转型”及“向上转型”操作


定义泛型类

Object类为最上层的父类,通常为了使程序更为通用,设计程序时使传入的值与返回的值都以Object类型为主。以至于运行时将会发生ClassCastException异常

(1)编写泛型时,需要定义泛型类型<T>
(2)静态方法不能引用泛型类型<T>,必须定义其他类型(例如<K>)来实现静态泛型方法
(3)泛型可以同时定义多种类型。例如Map<K, V>

编写泛型类比普通类要复杂。通常来说,泛型类一般用在集合类中

类名<T>

T代表一个类型的名称

<T> T与T的区别

泛型描述
<T> T表示返回值是一个泛型,传递什么,就返回什么类型的数据
T表示限制传递的参数类型

常规用法

定义泛型类时声明多个类型

MutiOverClass<T1,T2>

MutiOverClass:泛型类名称
T1、T2为可能被定义的类型,这样在实例化指定类型的对象时就可以指定多个类型

1.基本类型

按照某种类型:String,来编写类。然后,标记所有的特定类型(String)

public class Pair {
    private String first;
    private String last;
    public Pair(String first, String last) {
        this.first = first;
        this.last = last;
    }
    public String getFirst() {
        return first;
    }
    public String getLast() {
        return last;
    }
}

2.泛型类

将基本类型String替换为T

public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
    }
}

3.静态方法

特别注意:泛型类型<T>不能用于静态方法,会导致编译错误,无法在静态方法的方法参数和返回类型上使用泛型类型T
可以在static修饰符后面加一个<T>,编译就能通过,但实际上,这个<T>和Pair<T>类型的<T>已经没有任何关系了

public class Pair<T> {
	private T first;
	private T last;

	public Pair(T first, T last) {
		this.first = first;
		this.last = last;
	}

	// 可以编译通过:
	public static <T> Pair<T> create(T first, T last) {
		return new Pair<T>(first, last);
	}
}

对于静态方法,可以单独改写为“泛型”方法,只需要使用另一个类型即可。对于上面的create()静态方法,应该把它改为另一种泛型类型

public class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public T getLast() { ... }

    // 静态泛型方法应该使用其他类型区分:
    public static <K> Pair<K> create(K first, K last) {
        return new Pair<K>(first, last);
    }
}

这样才能清楚地将静态方法的泛型类型实例类型的泛型类型区分开


4.多个泛型类型

泛型还可以定义多种类型。Pair类总是存储两个类型不一样的对象,就可以使用类型<T, K>

public class Pair<T, K> {
    private T first;
    private K last;
    
    public Pair(T first, K last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() { ... }
    public K getLast() { ... }
}

使用的时候,需要指出两种类型

Pair<String, Integer> p = new Pair<>("test", 123);

Java标准库的Map<K, V>就是使用两种泛型类型的例子。它对Key使用一种类型,对Value使用另一种类型


擦拭法

(1)泛型是一种类似“模板代码”的技术,不同语言的泛型实现方式不一定相同
(2)Java语言的泛型实现方式是擦拭法(Type Erasure)
擦拭法:虚拟机对泛型其实一无所知,所有的工作都是编译器做的

编译时

1.编译器看到的代码

public class Pair<T> {
	private T first;
	private T last;

	public T getFirst() {
		return first;
	}

	public T getLast() {
		return last;
	}
}

2.虚拟机执行的代码

虚拟机根本不知道泛型

public class Pair {
    private Object first;
    private Object last;
    public Object getFirst() {
        return first;
    }
    public Object getLast() {
        return last;
    }
}

也就是将<T>类型,转换为了Object

Java使用擦拭法实现泛型,导致
(1)编译器把类型<T>视为Object
(2)编译器根据<T>实现安全的强制转型


运行时

1.编译器写的代码

使用泛型的时候,编写的代码也是编译器看到的代码

Pair<String> p = new Pair<>("Hello", "world");
String first = p.getFirst();
String last = p.getLast();

2.虚拟机执行代码

而虚拟机执行的代码并没有泛型

Pair p = new Pair<>("Hello", "world");
String first = (String) p.getFirst();
String last = (String) p.getLast();

所以,Java的泛型是由编译器在编译时实行的,编译器内部永远把所有类型T视为Object处理,但是,在需要转型的时候,编译器会根据T的类型自动实行安全地强制转型


Java泛型的局限

Java泛型的实现方式(擦拭法),就知道了Java泛型的局限

局限一(不能是基本类型)

<T>不能是基本类型。例如int,因为实际类型是Object,Object类型无法持有基本类型

Pair<int> p = new Pair<>(1, 2); // compile error!

局限二(获取的Class实例都是同一个)

无法取得带泛型的Class

class Pair<T> {
    private T first;
    private T last;
    public Pair(T first, T last) {
        this.first = first;
        this.last = last;
    }
    public T getFirst() {
        return first;
    }
    public T getLast() {
        return last;
	}
}

Pair<String> p1 = new Pair<>("Hello", "world");
Pair<Integer> p2 = new Pair<>(123, 456);
Class c1 = p1.getClass();
Class c2 = p2.getClass();
System.out.println(c1==c2); // true
System.out.println(c1==Pair.class); // true

因为T是Object,对Pair<String>和Pair<Integer>类型获取Class时,获取到的是同一个Class,也就是Pair类的Class
换句话说,所有泛型实例,无论T的类型是什么,getClass()返回同一个Class实例,因为编译后它们全部都是Pair<Object>


局限三(不能intanceof判断带泛型的Class)

无法判断带泛型的Class

Pair<Integer> p = new Pair<>(123, 456);
// Compile error:
if (p instanceof Pair<String>.class) {}

原因和前面一样,并不存在Pair<String>.class,而是只有唯一的Pair.class


局限四(不能实例化T类型)

不能实例化T类型

public class Pair<T> {
    private T first;
    private T last;
    
    public Pair() {
        // Compile error:
        first = new T();
        last = new T();
    }
}

上述代码无法通过编译,因为构造方法的两行语句

first = new T();
last = new T();

擦拭后实际上变成了:

first = new Object();
last = new Object();

这样一来,创建new Pair<String>()和创建new Pair<Integer>()就全部成了Object,显然编译器要阻止这种类型不对的代码

要实例化T类型,必须借助额外的Class<T>参数:

public class Pair<T> {
    private T first;
    private T last;
    public Pair(Class<T> clazz) {
        first = clazz.newInstance();
        last = clazz.newInstance();
    }
}

上述代码借助Class<T>参数并通过反射来实例化T类型,使用的时候,也必须传入Class<T>

Pair<String> pair = new Pair<>(String.class);

因为传入了Class<String>的实例,所以借助String.class就可以实例化String类型


不恰当的覆写方法(需要避开与Object的冲突)

有些时候,一个看似正确定义的方法会无法通过编译

public class Pair<T> {
    public boolean equals(T t){
        return this == t;
    }
}

这是因为,定义的equals(T t)方法实际上会被擦拭成equals(Object t),而这个方法是继承自Object的,编译器会阻止一个实际上会变成覆写的泛型方法定义
换个方法名,避开与Object.equals(Object)的冲突就可以成功编译

public class Pair<T> {
    public boolean same(T t) {
        return this == t;
    }
}

泛型继承

一个类可以继承自一个泛型类,使用的时候,因为子类IntPair并没有泛型类型,所以,正常使用即可

public class IntPair extends Pair<Integer> {}
IntPair ip = new IntPair(1 , 2); 

在继承了泛型类型的情况下,子类可以获取父类的泛型类型。例如:IntPair可以获取到父类的泛型类型Integer

class Pair<T> {
	private T first;
	private T last;

	public Pair(T first, T last) {
		this.first = first;
		this.last = last;
	}

	public T getFirst() {
		return first;
	}

	public T getLast() {
		return last;
	}
}

class IntPair extends Pair<Integer> {
	public IntPair(Integer first, Integer last) {
		super(first, last);
	}
}

Class<IntPair> clazz = IntPair.class;
Type t = clazz.getGenericSuperclass();
if(t instanceof ParameterizedType) {
	ParameterizedType pt = (ParameterizedType) t;
	// 可能有多个泛型类型
	Type[] types = pt.getActualTypeArguments();
	// 取第一个泛型类型
	Type firstType = types[0];
	Class<?> typeClass = (Class<?>) firstType;
	System.out.println(typeClass); // Integer
}

Java的类型系统结构
在这里插入图片描述

(1)Java的泛型是采用擦拭法实现
(2)擦拭法决定了泛型<T>

条件示例
①不能是基本类型int
②不能获取带泛型类型的ClassPair<String>.class
③不能判断带泛型类型的类型x instanceof Pair<String>
④不能实例化T类型new T()

(3)泛型方法要防止重复定义方法:public boolean equals(T obj)
(4)子类可以获取父类的泛型类型<T>

extends通配符(其子类或本身,下界)

<? extends Number>的泛型定义称之为上界通配符(Upper Bounds Wildcards),即把泛型类型T的上界限定在Number了,?代表的是Number或Number的子类

除了可以传入Pair<Integer>类型,我们还可以传入Pair<Double>类型,Pair<BigDecimal>类型等等,因为Double和BigDecimal都是Number的子类

class Pair<T> {
	private T first;
	private T last;

	public Pair(T first, T last) {
		this.first = first;
		this.last = last;
	}

	public T getFirst() {
		return first;
	}

	public T getLast() {
		return last;
	}
}

public static void main(String[] args) {
	Pair<Integer> p = new Pair<>(123, 456);
	int n = add(p);
	System.out.println(n);
}

static int add(Pair<? extends Number> p) {
	Number first = p.getFirst();
	Number last = p.getLast();
	return first.intValue() + last.intValue();
}

1.作用

如果考察Java标准库的java.util.List<T>接口,它实现的是一个类似“可变数组”的列表,主要功能包括

public interface List<T> {
    int size(); // 获取个数
    T get(int index); // 根据索引获取指定元素
    void add(T t); // 添加一个新元素
    void remove(T t); // 删除一个已有元素
}

2.限定T类型

定义泛型类型Pair<T>的时候,也可以使用extends通配符来限定T的类型

public class Pair<T extends Number> {}

非Number类型将无法通过编译

(1)使用类似<? extends Number>通配符作为方法参数时表示
方法内部可以调用获取Number引用的方法,例如:Number n = obj.getFirst();;
方法内部无法调用传入Number引用的方法(null除外),例如:obj.setFirst(Number n);。
即一句话总结:使用extends通配符表示可以读,不能写
(2)使用类似<T extends Number>定义泛型类时表示:泛型类型限定为Number以及Number的子类


super通配符(其父类或本身,下界)

Pair<? super Integer>表示,方法参数接受所有泛型类型为Integer或Integer父类的Pair类型


extends和super比较

extendssuper
作为方法参数允许调用读方法T get()获取T的引用,但不允许调用写方法set(T)传入T的引用(传入null除外)允许调用写方法set(T)传入T的引用,但不允许调用读方法T get()获取T的引用(获取Object除外)

一个是允许读不允许写,另一个是允许写不允许读


PECS(Producer Extends Consumer Super)原则

如果需要返回T,它是生产者(Producer),要使用extends通配符;如果需要写入T,它是消费者(Consumer),要使用super通配符
总结出一条规律“Producer Extends,Consumer Super”:返回T(读)用extends,写入T(写)super
(1)“Producer Extends”:如果需要一个只读List,用它来Producer T,那么使用? extends T
(2)“Consumer Super”:如果需要一个只写List,用它来Consumer T,那么使用? super T
(3)如果需要同时读取以及写入,那么就不能使用通配符了


无限定通配符

<? extends T>和<? super T>作为方法参数的作用。实际上,Java的泛型还允许使用无限定通配符(Unbounded Wildcard Type),即只定义一个?

<?>通配符既没有extends,也没有super,因此:

(1)不允许调用set(T)方法并传入引用(null除外)
(2)不允许调用T get()方法并获取T引用(只能获取Object引用)
既不能读,也不能写,只能做一些null判断

(1)使用类似<? super Integer>通配符作为方法参数时表示
方法内部可以调用传入Integer引用的方法,例如:obj.setFirst(Integer n);;
方法内部无法调用获取Integer引用的方法(Object除外),例如:Integer n = obj.getFirst();。
即使用super通配符表示只能写不能读
(2)使用extends和super通配符要遵循PECS原则
(3)无限定通配符<?>很少使用,可以用<T>替换,同时它是所有<T>类型的超类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/455548.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关闭默认共享服务

目录 1.从网络上关闭共享协议2.禁用共享服务3.关闭防火墙共享端口4.脚本自启动删除默认共享5.修改注册表彻底屏蔽共享 共5种方法&#xff0c;推荐前三种搭配。 1.从网络上关闭共享协议 打开网络和共享中心->本地连接->属性把" Microsoft 网络的文件和打印机共享&qu…

Word如何插入图片?最全方法总结(2023新版)

案例&#xff1a;Word如何插入图片 【我最近在写毕业论文&#xff0c;需要在Word文档里添加一些图片&#xff0c;想问问大家Word如何插入图片呀&#xff1f;感谢回答&#xff01;】 Microsoft Word是一个广泛使用的文字处理软件&#xff0c;在使用中如果插入图片可以很好的丰…

优思学院|精益生产和六西格玛如何把控质量?

精益生产是一种流程改善方法&#xff0c;旨在最大程度地减少浪费和提高效率。在实施精益生产时&#xff0c;质量控制是一个重要的因素&#xff0c;因为不合格的产品会导致延误和浪费。优思学院在本文将探讨精益生产和六西格玛管理理论如何控制质量&#xff0c;以及解决产品质量…

poi-tl简介与文本/表格和图片渲染

一、poi-tl简介 下面简介来自官方文档。 官方文档&#xff1a;http://deepoove.com/poi-tl/#_why_poi_tl 1、简介 poi-tl&#xff08;poi template language&#xff09;是Word模板引擎&#xff0c;使用Word模板和数据创建很棒的Word文档。 poi-tl是一个基于Apache POI的Word模…

【Python】【进阶篇】5、Django Admin后台管理系统

目录 5、Django Admin后台管理系统1. 后台管理系统的重要性2. 了解Django后台管理功能 5、Django Admin后台管理系统 Django 的后台管理系统是非常出色的&#xff0c;新建项目以后&#xff0c;Django 就为我们设置好了后台管理系统的各种功能&#xff0c;本节我们将一起认识它…

SQLServer:Win/Linux环境安装及一键部署脚本

1. Win安装SQLServer CSDN已有完整安装流程&#xff0c;亲测可用。----》Windows安装SQLServer流程 2. Linux安装 SQLServer 2.1 设置镜像 curl https://packages.microsoft.com/config/rhel/7/mssql-server-2017.repo > /etc/yum.repos.d/mssql-server.repo 2.2 通过y…

Adobe国际认证证书有用吗?

Adobe国际认证又称为Adobe认证(英文:Adobe Certified Professional)是Adobe公司CEO签发的权威国际认证体系,旨在为用户提供Adobe软件的专业认证。 该体系基于Adobe核心技术及岗位实际应用操作能力的测评体系得到国际ISTE协会的认可&#xff0c;并在全球 148 各国家推广&#x…

心理预期太大,容易失望

心理预期太大&#xff0c;是做事的障碍 心理预期与标准有关&#xff1a;亚马逊创始人谈标准 趣讲大白话&#xff1a;对事要有合理的心理预期 【趣讲信息科技144期】 **************************** 亚马逊创始人贝索斯在《长期主义》中 对高标准的四大要素&#xff1a; 1.可以通…

Node实现CSDN博客导出(后续)

前言 在2021年我实现了一个Node导出博客的功能&#xff1a;爬取接口及博客页面并导出为md文件格式。中途有许多迭代及优化以及解决了一些关键问题&#xff0c;写篇文章做个记录和review 博客更新功能 在原有的导出功能上增加了博客更新的功能&#xff0c;避免了每次都全部导…

Java7

Java7 &#xff08;一&#xff09;、集合体系&#xff08;二&#xff09;、Collection&#xff08;三&#xff09;、Collection的遍历方式3.1迭代器3.2增强for遍历3.3 Lambda表达式遍历 &#xff08;四&#xff09;、List&#xff08;五&#xff09;、数据结构5.1 栈5.2 队列5.…

C learning_8

猜数字游戏 猜数字游戏&#xff1a; 1.电脑会随机产生一个数 2.猜数字 a>猜大了&#xff0c;提醒猜大了&#xff0c;继续猜 b>猜小了&#xff0c;提醒猜小了&#xff0c;继续猜 c>猜对了&#xff0c;恭喜你&#xff0c;猜对了&#xff0c;游戏结束 3.玩完之后可以继续…

给httprunnermanager接口自动化测试平台加点颜色(一)

文章目录 一、背景1.1、部署过程略 二、使用过程2.1、新增接口列2.2、实现搜索效果 三、总结 一、背景 https://github.com/httprunner/HttpRunnerManager.git从github上找的接口测试平台&#xff0c;引入公司作为测试协同测试的平台&#xff0c;底层框架基于httprunner(reque…

SOLIDWORKS钣金设计需要考虑的折弯问题

设计需要考虑&#xff0c;究竟哪些是成型前加工&#xff0c;究竟哪些是成型后加工。 考虑工作制作工艺过程中&#xff0c;必须先折弯&#xff0c;后加工部分孔的情况有&#xff1a; 距离折弯边很近的圆孔&#xff0c;方孔&#xff0c;腰圆孔&#xff0c;螺纹等&#xff0c;下…

如何测试信号源或者发射机的回波损耗

信用源或者发射机的return loss测试过程 1.用网分线缆的第一步就是看线的抖动情况&#xff0c;后面还是要多注意 经过一系列排查后&#xff0c;选用两个抖动比较小的线缆&#xff0c;然后开始测试另外一台仪器。 2.检查测试仪器的输出功率&#xff0c;见图1 打开信号源或者发射…

项目管理-项目经理的5种权力

1、项目团队&#xff1a;是执行项目工作&#xff0c;以实现项目目标的一组人员&#xff0c;由为了完成项目而承担不同角色与职责的人员组成。 项目团队成员可能具备不同的技能&#xff0c;可能是全职的或兼职的&#xff0c;也可能随项目进展而增加或减少。尽管项目团队成员被分…

Mysql列的类型定义(二进制类型)

文章目录 前言一、类型图二、类型 1.BINARY和VARBINARY2.BIT类型3.BLOB类型三、实战建议总结 前言 二进制数据和文本数据在mysql 中的最大区别在于&#xff1a; 二进制类型存储原始的二进制数据(如图片&#xff0c;视频&#xff0c;exe文件等)。文本类型(TEXT)用来存储字符字…

【13】SCI易中期刊推荐——计算机工程 | 电子与电气(中科院4区)

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨ 📚📚>>>人工智能 | 计算机视觉…

【嵌入式】HC32F定时器PWM输出+PAC芯片实现模拟DA输出

目录 一 项目背景 二 原理说明 三 设计实现——定时器初始化 四 设计实现——PWM输出 五 梳理总结 一 项目背景 目前使用了TI的DAC芯片DAC7311&#xff0c;将MCU的4-20/0-20数据转化电压信号&#xff0c;经由一系列电路&#xff0c;最终输出4-20/0-20mA电流输出。 但是限于…

执行composer update报错(Segmentation fault php “${dir}/composer.phar“ “$@“)

在php项目中执行composer update&#xff0c;出现如图报错 可在php.ini文件中开启xdebug扩展&#xff0c;即可更新成功

(数字图像处理MATLAB+Python)第六章图像平滑-第二节:频域平滑滤波

文章目录 一&#xff1a;理想低通滤波&#xff08;1&#xff09;概述&#xff08;2&#xff09;程序 二&#xff1a;巴特沃斯低通滤波&#xff08;1&#xff09;概述&#xff08;2&#xff09;程序 三&#xff1a;指数低通滤波&#xff08;1&#xff09;概述&#xff08;2&…