JAVASE提高 -- Java泛型

article/2025/10/13 19:58:54

1. 泛型类

(1)使用语法
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();

(2)Java1.7以后,后面的<>中的具体的数据类型可以省略不写
类名<具体的数据类型> 对象名 = new 类名<>();

注意事项 :

  • 泛型类,如果没有指定具体的数据类型,此时,操作类型是Object
  • 泛型的类型参数只能是类类型,不能是基本数据类型, 如 int , float
  • 泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型
package com.hjy.pojo;import lombok.Data;@Data
public class MyGeneric<T> {private T value;
}
public class Main {public static void main(String[] args) {MyGeneric<String> myGeneric = new MyGeneric<>();MyGeneric<Integer> myGeneric2 = new MyGeneric<>();myGeneric.setValue("hello");System.out.println("myGeneric = " + myGeneric);System.out.println(myGeneric.getClass() == myGeneric2.getClass()); // true}
}

2. 泛型类派生子类

存在两种请求

  1. 子类也是泛型类,子类和父类的泛型类型要一致
    class ChildGeneric<T> extends Generic<T>
  2. 子类不是泛型类,父类要明确泛型的数据类型
    class ChildGeneric extends Generic<String>
//父类
public class Parent<E> {private E value;public E getValue() {return value;}public void setValue(E value) {this.value = value;}
}/*** 泛型类派生子类,子类也是泛型类,那么子类的泛型标识要和父类一致。* @param <T>*/
public class ChildFirst<T> extends Parent<T> {@Overridepublic T getValue() {return super.getValue();}
}
/*** 泛型类派生子类,如果子类不是泛型类,那么父类要明确数据类型*/
public class ChildSecond extends Parent<Integer> {@Overridepublic Integer getValue() {return super.getValue();}@Overridepublic void setValue(Integer value) {super.setValue(value);}
}

3. 泛型接口

泛型接口的定义语法:

interface 接口名称 <泛型标识,泛型标识,…> {泛型标识 方法名(); .....
}

使用和泛型类的派生对象一样, 也分两种情况

  • 实现类也是泛型类,实现类和接口的泛型类型要一致
/*** 泛型接口* @param <T>*/
public interface Generator<T> {T getKey();
}
package com.hjy.pojo;import lombok.Data;
/*** 泛型接口的实现类,是一个泛型类,* 那么要保证实现接口的泛型类泛型标识包含泛型接口的泛型标识* @param <T>* @param <E>*/
@Data
public class Pair<T,E> implements Generator<T>{private T key;private E value;@Overridepublic T getKey() {return key;}
}
  • 实现类不是泛型类,接口要明确数据类型
/*** 实现泛型接口的类,不是泛型类,需要明确实现泛型接口的数据类型。*/
public class Apple implements Generator<String> {@Overridepublic String getKey() {return "hello generic";}
}

4. 泛型方法

泛型方法是在调用方法的时候指明泛型的具体类型。

语法: 修饰符 <T,E, ...> 返回值类型 方法名(形参列表) { 方法体... }

  • public与返回值中间非常重要,可以理解为声明此方法为泛型方法
  • 只有声明了的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法
  • < T >表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
  • 与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
  • 泛型方法声明的泛型标志和类独立, 但是如果没有发生重叠, 泛型方法是可以使用类定义的泛型标志的
  • 泛型方法是可以声明为静态方法的, 如果static方法要使用泛型能力,就必须使其成为泛型方法
package com.hjy.pojo;import lombok.Data;import java.util.List;
import java.util.Random;@Data
public class MyGeneric<T> {private T value;// 使用类的泛型标志public <R> T hello1(R r) {return value;}// 自定义的泛型标志覆盖类的泛型标志public <T> T hello2(List<T> list) {Random random = new Random();int i = random.nextInt(list.size());return list.get(i);}// 声明静态方法public static <T> T hello3(T t) {return t;}
}
package com.hjy;import com.hjy.pojo.Children;
import com.hjy.pojo.MyGeneric;
import com.hjy.pojo.Pair;import java.util.ArrayList;
import java.util.List;public class Main {public static void main(String[] args) {MyGeneric<String> g = new MyGeneric<>();g.setValue("hello");System.out.println(g.hello1(1));List<String> list = new ArrayList<>();list.add("笔记本");list.add("手机");list.add("哈哈");System.out.println(g.hello2(list));System.out.println(MyGeneric.hello3("he"));}
}

5. 类型通配符

在java中,数组是可以协变的,比如dog extends Animal,那么Animal[] 与dog[]是兼容的。而集合是不能协变的,也就是说List<Animal>不是List<dog>的父类,这时候就可以用到通配符了。

  • 类型通配符一般是使用"?"代替具体的类型实参。
  • 所以,类型通配符是类型实参,而不是类型形参
public class Main {public static void main(String[] args) {List<Integer> list = new ArrayList<>();h1(list); //报错h2(list); // 正常}public static void h1(List<Number> list) {}public static void h2(List<Integer> list) {}
}

如上, Number 是 Integer 类的父类, h1(list) 按道理来说不会报错, 可以指定泛型就是认死理 ,不是指定的类型就报错, 这个时候我们就可以使用 ? 类型通配符来操作

public class Main {public static void main(String[] args) {List<Integer> list = new ArrayList<>();h1(list); //报错h2(list); // 正常}public static void h1(List<?> list) {}public static void h2(List<?> list) {for (Object o : list) {}}
}

现在就不报错了, 可是单纯的使用通配符会让其类型默认为 Object, 我们有什么办法解决呢 ?

类型通配符的上限

  • 语法:
    类/接口<? extends 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的子类类型,多用于函数参数

class Animal {}class Cat extends Animal{}class MiniCat extends Cat{}public class Main {public static void main(String[] args) {List<Animal> list1 = new ArrayList<>();List<Cat> list2 = new ArrayList<>();List<MiniCat> list3 = new ArrayList<>();show(list1); // 报错show(list2); // 正常show(list3); //正常}public static void show(List<? extends Cat> list) {// 报错, 因为这时候它只知道你是 Cat 的子类, 但是不知道你是 Cat 具体的哪个子类// list.add(new Cat());}}

类型通配符的下限

  • 语法:
    类/接口<? super 实参类型>

要求该泛型的类型,只能是实参类型,或实参类型的父类类型。多用于函数参数

class Animal {}class Cat extends Animal{}class MiniCat extends Cat{}public class Main {public static void main(String[] args) {List<Animal> list1 = new ArrayList<>();List<Cat> list2 = new ArrayList<>();List<MiniCat> list3 = new ArrayList<>();show(list1); // 正常show(list2); // 正常show(list3); //报错}public static void show(List<? super Cat> list) {// 正常, 因为这时候它知道你是 Cat 的父类, 符合编译list.add(new Cat());}}

总结

我们要记住这么几个使用原则, 有人将其称为PECS(即"Producer Extends, Consumer Super", 网上翻译为"生产者使用extends, 消费者使用super", 我觉得还是不翻译的好). 也有的地方写作"in out"原则, 总的来说就是:

  • in或者producer就是你要读取出数据以供随后使用(想象一下List的get), 这时使用extends关键字, 固定上边界的通配符. 你可以将该对象当做一个只读对象;
  • out或者consumer就是你要将已有的数据写入对象(想象一下List的add), 这时使用super关键字, 固定下边界的通配符. 你可以将该对象当做一个只能写入的对象;
  • 当你希望in或producer的数据能够使用Object类中的方法访问时, 使用无边界通配符;
  • 当你需要一个既能读又能写的对象时, 就不要使用通配符了.

普通的泛型标志使用类型通配符的上限和下限

我们可以使用 extends 和 super 来缩小 泛型的范围

先看下面这个样例

@Data
public class MyGeneric<T> {private T value;public void he() {value.he();}}class Animal {public void he() {System.out.println("heh");}
}public class Main {public static void main(String[] args) {MyGeneric<Animal> generic = new MyGeneric<>();generic.setValue(new Animal());generic.he();}}

这样使用的话, 就会报错, 可是我想让他可以正常使用, 怎么办呢 ?

我们可以指定其上限为 Animal ,这样它即可以泛化 Animal 的子类, 也可以使用 Animal 中定义的方法

package com.hjy.pojo;class Animal {public void he() {System.out.println("heh");}
}package com.hjy.pojo;import lombok.Data;@Data
public class MyGeneric<T extends Animal> {private T value;public void he() {value.he();}}public class Main {public static void main(String[] args) {MyGeneric<Animal> generic = new MyGeneric<>();generic.setValue(new Animal());generic.he();}}

当然, 我们也可以使用 下限

@Data
public class MyGeneric<T> {private T value;// 锁定了参数一定是 T 的 父类或者它自己public void he(Comparator<? super T> comparator) {value.he();}}

6. 类型擦除

泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为–类型擦除。

类型擦除有四种情况, 下面我们以反射为例为大家解释

无限制类型擦除

在这里插入图片描述

package com.hjy;import java.lang.reflect.Field;class TheGeneric<T> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}public class Main {public static void main(String[] args) {TheGeneric<String> generic = new TheGeneric<>();// 获取字节码Class<? extends TheGeneric> aClass = generic.getClass();Field[] fields = aClass.getDeclaredFields();for (Field field : fields) {System.out.println(field.getName() + ":" + field.getType().getSimpleName());}}}

结果:

value:Object

有限制类型擦除

在这里插入图片描述

package com.hjy;import java.lang.reflect.Field;class TheGeneric<T extends String> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}
}public class Main {public static void main(String[] args) {TheGeneric<String> generic = new TheGeneric<>();// 获取字节码Class<? extends TheGeneric> aClass = generic.getClass();Field[] fields = aClass.getDeclaredFields();for (Field field : fields) {System.out.println(field.getName() + ":" + field.getType().getSimpleName());}}}
value:String

擦除方法中类型定义的参数

在这里插入图片描述

package com.hjy;import java.lang.reflect.Field;
import java.lang.reflect.Method;class TheGeneric<T extends String> {private T value;public T getValue() {return value;}public void setValue(T value) {this.value = value;}public <T extends Number> T hehe(T t) {return t;}
}public class Main {public static void main(String[] args) {TheGeneric<String> generic = new TheGeneric<>();// 获取字节码Class<? extends TheGeneric> aClass = generic.getClass();Method[] methods = aClass.getDeclaredMethods();for (Method method : methods) {System.out.println(method.getName() + ":"+ method.getReturnType().getSimpleName());}}}
hehe:Number
getValue:String
setValue:void

桥接方法

在这里插入图片描述

package com.hjy;import java.lang.reflect.Field;
import java.lang.reflect.Method;interface Info<T> {T info(T var);
}class TheGeneric implements Info<Integer> {@Overridepublic Integer info(Integer var) {return null;}
}public class Main {public static void main(String[] args) {TheGeneric generic = new TheGeneric();// 获取字节码Class<? extends TheGeneric> aClass = generic.getClass();Method[] methods = aClass.getDeclaredMethods();for (Method method : methods) {System.out.println(method.getName() + ":"+ method.getReturnType().getSimpleName());}}}
info:Integer
info:Object

7. 泛型与数组

  • 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
ArrayList<String>[] listArr = new ArrayList<5>(); //会报错

但是我们可以先创建出不带泛型的对象, 再将这个赋值给带泛型的对象

ArrayList[] list = new ArrayList[5];
ArrayList<String>[] listArr = list;
// 或者
ArrayList<String>[] listArr = new ArrayList[5];
  • 可以通过java.lang.reflect.Array的newInstance(Class,int)创建T[]数组
public class Fruit<T> {private T[] array;public Fruit(Class<T> clz, int length){//通过Array.newInstance创建泛型数组array = (T[])Array.newInstance(clz, length);}
}

http://chatgpt.dhexx.cn/article/WxlgClqL.shtml

相关文章

【NLP】如何提高模型泛化能力?以中文NER为例

我们知道模型的泛化能力是很重要的&#xff0c;如果一个模型具有很好的泛化性能&#xff0c;那么它往往能够在没有见过的数据上表现良好。以中文命名实体识别为例&#xff0c;在用于评估模型泛化性能的数据集中&#xff0c;我们可能忽略了数据偏差对模型泛化的影响。 论文标题&…

C#提升(一、泛型)

一、什么是泛型 泛型&#xff0c;即“参数化类型” 我们来看以下代码&#xff0c;目的很明确&#xff0c;就是显示参数类型&#xff0c;这种类似的代码或者说只有参数类型不同&#xff0c;但是功能相同时&#xff0c;我们如何让代码写的更优雅&#xff1f; 在泛型没有出现的…

深度学习提高泛化能力的技术

LeetCode题目记录 1.泛化能力&#xff08;generalization&#xff09; 2.正则化&#xff08;regularization&#xff09;2.1 正则化方法 1.泛化能力&#xff08;generalization&#xff09; 对于模型&#xff0c;我们不仅要求它对训练数据集有很好的拟合&#xff08;训练误差&a…

lecture9-提高模型泛化能力的方法

HInton第9课&#xff0c;这节课没有放论文进去。。。。。如有不对之处还望指正。话说hinton的课果然信息量够大。推荐认真看PRML《Pattern Recognition and Machine Learning》。 摘自PRML中22页。 正文&#xff1a; 一、提高泛化方法的概述 在这部分中&#xff0c;将会介绍…

思考深度学习的泛化能力

神经网络通过记忆学习 传统观点论文观点论文实验 神经网络 不 通过记忆学习参考资料 深度神经网络往往带有大量的参数&#xff0c;但依然表现出很强的泛化能力&#xff08;指训练好的模型在未见过的数据上的表现&#xff09;。 深度神经网络为何会拥有如此强的泛化能力&…

SQL提升(一)

Sql不常见关键字提升 一、前言 Sql是最重要的关系数据库操作语言&#xff0c;现在基本上任何与数据库相关的操作都离不开sql。所以说sql功能是很强大的。 我们常用的sql关键字不外乎 group by;in; where; from; update……等&#xff0c;这些关键字有时组成sql的基础。 但是…

越大的数据集训练,网络泛化能力越强

虽然这个直观很好理解&#xff0c;但我想找这个论点的论文支持&#xff0c;如果再能找张图就好了&#xff0c;下面是过程 首先是On Large-Batch Training for Deep Learning: Generalization Gap and Sharp Minima这篇经典文章&#xff0c;里面的这张flat minima和sharp minim…

浅谈深度学习泛化能力

谷歌最近出品的82页论文《ON THE GENERALIZATION MYSTERY IN DEEP LEARNING》&#xff0c;在此我简单归纳下论文的思想&#xff0c;有兴趣的看看原论文。论文链接&#xff1a;github.com/aialgorithm/Blog 一、DNN泛化能力的问题 论文主要探讨的是&#xff0c; 为什么过参数的…

深入理解泛化

文章目录 1.引言2.泛化的定义3.数据集分类4.泛化能力分类5.从训练过程来理解泛化参考资料 1.引言 什么是泛化呢&#xff1f; 先举个栗子&#xff1a; 小明和小李都上了高三。小明头脑机灵&#xff0c;一边刷着五年高考三年模拟一边总结做题规律&#xff0c;而小李一门心思刷题…

机器学习-泛化能力

目录 1.什么是泛化能力 2.什么是好的机器学习模型的提出 3.泛化误差 4.模型泛化能力的评价标准 4.提高泛化能力 5.举例 6.相关引用文献 1.什么是泛化能力 百度百科解释&#xff1a;机器学习算法对新鲜样本的适应能力。 更加具体的解释&#xff1a;学习到的模型对未知…

机器学习中的泛化能力

模型的泛化能力&#xff1a;指机器学习算法对新鲜样本的适应能力。 学习的目的&#xff1a;学到隐含在数据背后的规律&#xff0c;对具有同一规律的学习集以外的数据&#xff0c;经过训练的网络也能给出合适的输出&#xff0c;该能力称为泛化能力。 由此可见&#xff0c;经训练…

神经网络泛化的能力因素,神经网络泛化的能力差

1、BP神经网络当中 所提到的泛化能力是指什么&#xff1f; 就是外推的能力。 很多时候训练的网络对于训练的数据能很好的拟合&#xff0c;但是对于不在训练集内的数据拟合就很差强人意了。这种情况就叫泛化能力----差。也就是说可能你的网络存在过拟合的现象。 谷歌人工智能写…

【深度学习】常见的提高模型泛化能力的方法

前言 模型的泛化能力是其是否能良好地应用的标准&#xff0c;因此如何通过有限的数据训练泛化能力更好的模型也是深度学习研究的重要问题。仅在数据集上高度拟合而无法对之外的数据进行正确的预测显然是不行的。本文将不断总结相关的一些方法。 一、模型角度 Dropout 首先随…

TCP/IP报文格式

1、IP报文格式    IP协议是TCP/IP协议族中最为核心的协议。它提供不可靠、无连接的服务&#xff0c;也即依赖其他层的协议进行差错控制。在局域网环境&#xff0c;IP协议往往被封装在以太网帧&#xff08;见本章1.3节&#xff09;中传送。而所有的TCP、UDP、ICMP、IGMP数据…

TCP首部报文段格式

最近《计算机网络》这本书看到了传输层的 TCP 协议&#xff0c;因为TCP 的全部功能都体现在它的首部中&#xff0c;因此觉得有必要将这些知识梳理一下。 首先TCP 是面向字节流的。这个流指的是流入到进程或从进程流出的字节序列。面向字节流的含义是&#xff1a;应用程序与 TC…

ARP报文格式详解

ARP 协议包&#xff08;ARP 报文&#xff09;主要分为 ARP 请求包和 ARP 响应包&#xff0c;本节将介绍 ARP 协议包的格式。 ARP 报文格式 ARP 协议是通过报文进行工作的&#xff0c;ARP 报文格式如图所示。 ARP 报文总长度为 28 字节&#xff0c;MAC 地址长度为 6 字节&…

CAN的报文格式

CAN的报文格式 在总线中传送的报文&#xff0c;每帧由7部分组成。CAN协议支持两种报文格式&#xff0c;其唯一的不同是标识符&#xff08;ID&#xff09;长度不同&#xff0c;标准格式为11位&#xff0c;扩展格式为29位。 在标准格式中&#xff0c;报文的起始位称为帧起始&am…

IPv4报文格式详解和报文示例

目录 一&#xff0c;IP数据报文的组成 二&#xff0c;IP报文格式 三&#xff0c;IPv4报文示例 作者&#xff1a;柒烨带你飞 一&#xff0c;IP数据报文的组成 一个IP数据报文都是由首部和数据两部分组成。 每个 IP 数据报都以一个 IP 报头开始。IP 报头中包含大量信息&#…

ICMP报文格式解析

ICMP报文的格式类型总共分为三大类&#xff1a; 1、差错报文 2、控制报文 3、查询报文 上图是ICMP报文的基本格式&#xff0c;上面提到的三种ICMP报文均有“类型&#xff0c;代码和校验和”三个字段&#xff0c;后面还有4个字节是根据不同的报文类型而有不同的格式&#xff…

常见网络报文数据包格式

当我们应用程序用TCP传输数据的时候&#xff0c;数据被送入协议栈中&#xff0c;然后逐个通过每一层&#xff0c;知道最后到物理层数据转换成比特流&#xff0c;送入网络。而再这个过程中&#xff0c;每一层都会对要发送的数据加一些首部信息。整个过程如下图。 以太网帧格式 以…