一、什么是泛型

  早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序并不安全

  针对List、Set、Map等集合类型,它们对存储的元素类型是没有任何限制的。例如向List中存储Dog类型的对象,但是有人把Cat对象也存储到这个List中了,那么在编译上是没有任何语法错误的。

  所有使用该泛型参数的地方都被统一化,保证类型一致。如何未指定具体类型,默认是Object类型。集合体系中的所有类都增加了泛型,泛型也主要用于集合

二、泛型类

  泛型类就是把泛型定义在类class ClassName<T>上,用户使用该类的时候,才把类型明确下来。这样的话,用户明确了什么类型,该类就代表着什么类型,用户在使用的时候就不用担心强转的问题,和运行时转换异常的问题了。

package com.harley.genericity;

import lombok.Data;

/**
* @author harley
* @since 2024-03-20 17:28:39
*/
public class ClassGeneric { public static void main(String[] args) { ObjectUtil<String> stringUtil = new ObjectUtil<>();
stringUtil.setObj("Harley");
System.out.println(stringUtil.getObj()); ObjectUtil<Integer> integerObj = new ObjectUtil<>();
integerObj.setObj(11);
System.out.println(integerObj.getObj()); } @Data
static class ObjectUtil<T>{
private T obj;
}
}

编译报错

三、泛型方法

  除了在类上使用泛型,我们可能就仅仅在某个方法上需要使用泛型,外界仅仅是关心该方法,不关心类其他的属性,这样的话,我们在整个类上定义泛型,未免就有些大题小做了。那么此时,我们可以采用泛型方法。调用方法,输入的参数是什么类型,T就是什么类型。

package com.harley.genericity;

/**
* @author harley
* @since 2024-03-20 17:48:47
*/
public class MethodGenericity { public static void main(String[] args) {
ObjectTool tool = new ObjectTool();
tool.show("Hello");
tool.show(21);
tool.show(true);
}
static class ObjectTool{
public <T> void show(T t){
System.out.println(t);
}
}
}

四、泛型类派生出的子类

  泛型类是拥有泛型这个特性的类,本质还是一个Java类,可以被继承(泛型类)或实现(泛型接口)。这里分为两种情况。

首先创建一个Inter接口,在该接口上使用泛型

interface Inter<T>{
void show(T t);
}

1、子类明确泛型类的类型参数变量

class InterImpl1 implements Inter<String>{
@Override
public void show(String s){
System.out.println(s);
}
}

2、子类不明确泛型类的的类型参数变量

class InterImpl2<T> implements Inter<T>{
@Override
public void show(T t){
System.out.println(t);
}
}

3、完整的代码

package com.harley.genericity;

/**
* @author harley
* @since 2024-03-20 18:06:46
*/
public class SubClassGenericity { public static void main(String[] args) { Inter<String> i = new InterImpl1();
i.show("你好"); Inter<Integer> i2 = new InterImpl2();
i2.show(1); } } interface Inter<T>{
void show(T t);
} class InterImpl1 implements Inter<String>{ @Override
public void show(String s) {
System.out.println(s);
}
} class InterImpl2<T> implements Inter<T>{ @Override
public void show(T t) {
System.out.println(t);
}
}

五、类型通配符

  List<T> 表示元素类型未知的List,它可以匹配任何类型的元素。声明List<?> list后,不能向集合中添加元素,因为无法确定集合的元素类型,唯一例外的是null。

1、List<T>的使用

package com.harley.genericity;

import java.util.ArrayList;
import java.util.List; /**
* @author harley
* @since 2024-03-20 18:15:00
*/
public class TypeWildcard1 { public static void main(String[] args) { List<String> list = new ArrayList<>();
list.add("路飞");
list.add("索隆");
list.add("娜美");
print1(list); List<Integer> list2 = new ArrayList<>();
list2.add(30000000);
list2.add(150000000);
list2.add(300000000);
print2(list2); } public static void print1(List<String> list){
for (String str :list) System.out.println(str);
} public static void print2(List<Integer> list){
for (Integer i: list) System.out.println(i);
}
}

2、List<?>的使用

package com.harley.genericity;

import java.util.ArrayList;
import java.util.List; /**
* @author harley
* @since 2024-03-20 18:22:21
*/
public class TypeWildcard2 { public static void main(String[] args) { List<String> list1 = new ArrayList<>();
list1.add("路飞");
list1.add("索隆");
list1.add("娜美");
print1(list1); List<Integer> list2 = new ArrayList<>();
list2.add(30000000);
list2.add(150000000);
list2.add(300000000);
print1(list2);
} public static void print1(List<?> list){
for (Object obj:list) System.out.println(obj);
}
}

3、List<?>的特点

因为无法确定集合的元素类型,所以不能向集合中添加元素,会报编译错误

唯一例外的是可以添加null

4、如何解决3中不能添加元素的问题

public static <T> void print1(List<T> list){
list.add(list.get(0));
list.add(null);
for (T t:list) System.out.println(t);
}

六、泛型的上限和下限

1、泛型的上限

  • 格式:类型名称 <? extends 类> 对象名称
  • 意义:只能接收该类型及其子类
package com.harley.genericity;

import java.util.ArrayList;
import java.util.List; /**
* @author harley
* @since 2024-03-20 18:45:08
* 指定类型通配符的上限 —— 返回值为类型的方法可以使用了
*/
public class TypeWildcard4 {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
List<Long> longList = new ArrayList<>();
print(intList);
print(longList);
} public static void print(List<? extends Number> list){
/*
因为调用本方法时传递的实参可能是List<Long>,也可能是List<Integer>
list.add(new Integer(100));
list.add(new Long(100));
*/
list.add(null); // 可以调用get()方法返回集合中的元素
for (int i = 0; i < list.size(); i++) {
Number obj = list.get(i);
System.out.println(obj);
}
}
}

2、泛型的下限

  • 格式:类型名称 <? super 类> 对象名称
  • 意义:只能接收该类型及其父类型
package com.harley.genericity;

import java.util.ArrayList;
import java.util.List; /**
* @author harley
* @since 2024-03-20 18:53:20
*/
public class TypeWildcard5 { public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
List<Number> numList = new ArrayList<>();
intList.add(1);
numList.add(2);
print(intList);
print(numList);
} /* list中可以放的类型为Integer及其父类Number类型 */
public static void print(List<? super Integer> list){
list.add(new Integer(100));
list.add(null);
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
System.out.println(obj);
}
}
}

七、类型擦除

  泛型是提供给javac编译器使用的,它可以作为类型的限制,让编译器在源代码级别上,挡住非法类型的数据。

  但是在JDK1.5之前没有泛型的概念,为了能够与之前版本代码兼容,编译器编译完带有泛型的java程序后,生成的class字节码文件中将不再带有泛型信息,这个过程称之为“擦除”。

“擦除”前的代码

public interface Animal<T>{
void eat(T t);
} public class Cat implements Animal<String>{
@Override
public void eat(String s){
System.out.println("cat eat" + s);
}
}

“擦除”后的代码

public interface Animal{
void eat(Object t);
} public class Cat implements Animal{
@Override
public void eat(String s){
System.out.println("cat eat" + s);
}
}

八、桥接方法的作用

  桥接方法是jdk1.5引入泛型后,为使java泛型方法生成的字节码与jdk1.5版本之前的字节码兼容由编译器自动生成的。可用method.isBridge()判断method是否是桥接方法。

自动生成的桥接方法

public interface Animal{
void eat(Object t);
} public class Cat implements Animal{
@Override
public void eat(Object s){
this.eat((String) s)
} public void eat(String s){
System.out.println("cat eat" + s)
} }

实例演示

1、创建一个Animal的接口

package com.harley.bridgemethod;

/**
* @author harley
* @since 2024-03-20 19:13:40
*/
public interface Animal<T> {
void eat(T t);
}

2、创建一个cat类实现Animal接口,并重写方法

package com.harley.bridgemethod;

/**
* @author harley
* @since 2024-03-20 19:13:35
*/
public class Cat implements Animal<String>{
@Override
public void eat(String s) {
System.out.println("cat eat " + s);
}
}

3、创建一个测试类,父类引用指向子类对象,并调用子类的eat方法

package com.harley.bridgemethod;

/**
* @author harley
* @since 2024-03-20 19:13:05
*/
public class BridgeMethodTest {
public static void main(String[] args) {
Animal<String> animal = new Cat();
animal.eat("fish");
}
}

4、使用泛型,解析方法类型

package com.harley.bridgemethod;

import java.lang.reflect.Method;
import java.util.Arrays; /**
* @author harley
* @since 2024-03-20 19:15:08
*/
public class MethodFactory {
public static void main(String[] args) {
Method[] methods = Cat.class.getDeclaredMethods();
for (Method method : methods) {
// 获取方法名称
String name = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
System.out.println(name + "("+ Arrays.toString(parameterTypes));
}
} }

执行结果证明了会自动生成桥接方法

— 要养成终生学习的习惯 —

Java进阶 - [1-5] 泛型的更多相关文章

  1. Java进阶(三十五)java int与integer的区别

    Java进阶(三十五)java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象 ...

  2. Java进阶之路

    Java进阶之路——从初级程序员到架构师,从小工到专家. 怎样学习才能从一名Java初级程序员成长为一名合格的架构师,或者说一名合格的架构师应该有怎样的技术知识体系,这是不仅一个刚刚踏入职场的初级程序 ...

  3. Java进阶4表达式中的陷阱

    Java进阶4表达式中的陷阱 20131103 表达式是Java中最基本的组成单元,各种表达式是Java程序员最司空见惯的内容,Java中的表达式并不是十分的复杂,但是也有一些陷阱.例如当程序中使用算 ...

  4. (转)Java进阶java int与Integer的区别

    Java进阶java int与Integer的区别 前言 int与Integer的区别从大的方面来说就是基本数据类型与其包装类的区别: int 是基本类型,直接存数值,而Integer是对象,用一个引 ...

  5. Java 进阶 hello world! - 中级程序员之路

    Java 进阶 hello world! - 中级程序员之路 Java是一种跨平台的语言,号称:"一次编写,到处运行",在世界编程语言排行榜中稳居第二名(TIOBE index). ...

  6. 《徐徐道来话Java》(2):泛型和数组,以及Java是如何实现泛型的

    数组和泛型容器有什么区别 要区分数组和泛型容器的功能,这里先要理解三个概念:协变性(covariance).逆变性(contravariance)和无关性(invariant). 若类A是类B的子类, ...

  7. Java进阶(五)Java I/O模型从BIO到NIO和Reactor模式

    原创文章,同步发自作者个人博客,http://www.jasongj.com/java/nio_reactor/ Java I/O模型 同步 vs. 异步 同步I/O 每个请求必须逐个地被处理,一个请 ...

  8. Java线程间通信方式剖析——Java进阶(四)

    原创文章,同步发自作者个人博客,转载请在文章开头处以超链接注明出处 http://www.jasongj.com/java/thread_communication/ CountDownLatch C ...

  9. Java进阶(三)多线程开发关键技术

    原创文章,同步发自作者个人博客,转载请务必以超链接形式在文章开头处注明出处http://www.jasongj.com/java/multi_thread/. sleep和wait到底什么区别 其实这 ...

  10. 当我们说线程安全时,到底在说什么——Java进阶系列(二)

    原创文章,同步发自作者个人博客,转载请以超链接形式在文章开头处注明出处http://www.jasongj.com/java/thread_safe/ 多线程编程中的三个核心概念 原子性 这一点,跟数 ...

随机推荐

  1. PySAGES结合CUDA SPONGE增强采样

    技术背景 在前面的一篇博客中,我们介绍过PySAGES这个增强采样软件的基本安装和使用方法.该软件类似于Plumed是一个外挂增强采样软件,但是PySAGES是基于Python语言和Jax框架来实现的 ...

  2. IPV6禁用导致 RabbitMQ 无法启动的问题

    问题现象 在开发的过程中遇到了 RabbitMQ 怎么也启动不起来的现象.查看 RabbitMQ 自身的启动日志,并没有发现有什么有用的报错信息,只是从某天开始就一直在打印重启的日志,尝试多次重启也不 ...

  3. Qt编写地图综合应用60-覆盖物坐标和搜索

    一.前言 地图应用中有时候需要开启悬浮工具栏,用户可以直接在地图上绘制矩形.多边形.圆形.线条等,于是需要提供一个函数接口,能够获取到用户绘制的这些图形形状对应的信息.比如坐标点.圆形的中心点和半径. ...

  4. Qt通用方法及类库1

    函数名 //桌面宽度高度 static int deskWidth(); static int deskHeight(); //程序文件名称+当前所在路径 static QString appName ...

  5. Qt开源作品7-高亮按钮控件

    一.前言 这个高亮按钮控件并非本人原创作品,是参考的Qt界的一个大师级人物公孙二狗的作品,各位有兴趣可以去搜索查看,在原作者的代码上,我只是改成了自己的控件的框架结构,然后完善了一些细节,比如增加了各 ...

  6. CSP-J2/S2 2024 游记

    前情提要:CSP-J/S 2023 写这篇文章的时候,心情比较复杂. 哎,结局还算圆满. 初赛 之前那个写的不好再写一遍() 两个都在 WFLS,也就是本校考 qaq. J 在大礼堂考,没啥好说的,太 ...

  7. 跟着源码学IM(九):基于Netty实现一套分布式IM系统

    本文作者小傅哥,原题"使用DDD+Netty,开发一个分布式IM(即时通信)系统".为了提升阅读体验,有大量修订和改动,感谢原作者. 0.系列文章 <跟着源码学IM(一):手 ...

  8. React中的 ref 及原理浅析

    前言 对于 ref 的理解,我们一部人还停留在用 ref 获取真实 dom 元素和获取组件层面上,但实际 ref 除了这两项功能之外,在使用上还有很多小技巧.本章我们就一起深入探讨研究一下 React ...

  9. python基础应用

    pip的使用 升级pip python3 -m pip install --upgrade pip 镜像源设置 查看镜像源 pip config list 指定镜像源更新依赖 pip3 install ...

  10. 今天记录一下管理系统中预览pdf的方法

    在管理系统中,有很多需要预览文件的操作,既方便用户查看又可以不用打开新的页面,我发现一个不错的方法,记录一下 <el-dialog title="" :visible.syn ...