什么是泛型:
泛型(Generics)是一种在编程语言中用于创建可重用代码的机制。它允许我们在定义类、接口或方法时使用一个或多个类型参数,以便在使用时指定具体的类型。
泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。
目的:
泛型的主要目的是增加代码的灵活性和可重用性,同时提高代码的安全性和可读性。通过使用泛型,我们可以在不同的场景下使用相同的代码逻辑,只需改变传入的类型参数即可。
JAVA推出泛型以前,程序员可以构建一个元素类型为Object
的集合,该集合能够存储任意的数据类型对象,而在使用该集合的过程中,需要程序员明确知道存储每个元素的数据类型,否则很容易引发ClassCastException异常。
Java泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。
以list集合为例
类型进行了参数化,其类型类型可以定义成许多种如下
好处在于不用我们自己进行类型转换,且编译期间检查类型,类型安全,消除强制转换。
package com.System;
/**
* @author cqh
* @date 2024/1/8
* @Description
*/
//T 是类型型参
public class FanXIng<T> {
// T 作为成员变量类型
private T key;
// T 作为参数类型
public FanXIng(T key){
this.key=key;
}
// T 作为返回类型
public T getKey() {
return key;
}
// T 作为参数类型
public void setKey(T key) {
this.key = key;
}
@Override
public String toString() {
return "FanXIng{" +
"key=" + key +
'}';
}
}
package com.System;
/**
* @author cqh
* @date 2024/1/8
* @Description
*/
public class FanXingTest {
public static void main(String[] args) {
FanXIng<String> stringFanXIng = new FanXIng<String>("你好!");
System.out.println("stringFanXIng = " + stringFanXIng.getKey());
FanXIng<Integer> integerFanXIng = new FanXIng<Integer>(100);
System.out.println("integerFanXIng = " + integerFanXIng.getKey());
}
}
泛型类不支持基本数据类型
同一泛型类根据不同类型创建的对象本质上是一个类型
?父类
子类
错误写法?
?正确写法
不写父类参数类型默认父类为Object类型?
子类可以泛型扩展但前提是包装有一个与父类相同的参数类型
泛型派生子类中如果子类不是泛型类而是继承父类,父类必须设置具体类型
错误
正确
实现类不是泛型类,接口要明确数据类型
实现类也是泛型类,实现类和接口的泛型类型要一致
?泛型接口
package com.System;
/**
* @author cqh
* @date 2024/1/8
* @Description
*/
public interface Generator<T> {
T getKey();
}
实现类不是泛型类
第一种泛型接口不使用类型参数 默认类型返回Object
package com.System;
/**
* @author cqh
* @date 2024/1/8
* @Description
*/
public class FanXingTest2 implements Generator {
@Override
public Object getKey() {
return null;
}
}
第二种一个确定泛型接口类型
实现类是泛型类 与泛型类的派生差不多
正确
package com.System;
/**
* @author cqh
* @date 2024/1/8
* @Description
*/
public class FanXingTest2<T> implements Generator<T> {
@Override
public T getKey() {
return null;
}
}
?
泛型扩充
泛型接口的实现类,是一个泛型类,那么要保证实现接口的泛型类泛型标识包含泛型接口的泛型标识
package com.System;
/**
* @author cqh
* @date 2024/1/8
* @Description
*/
public class FanXingTest2<T,E> implements Generator<T> {
private T key;
private E value;
public FanXingTest2() {
}
public FanXingTest2(T key, E value) {
this.key = key;
this.value = value;
}
@Override
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
public E getValue() {
return value;
}
public void setValue(E value) {
this.value = value;
}
@Override
public String toString() {
return "FanXingTest2{" +
"key=" + key +
", value=" + value +
'}';
}
}
错误?
泛型类,是在实例化类的时候指明泛型的具体类型。
泛型方法,是在调用方法的时候指明泛型的具体类型。
写法
?
使用?
?注意:
普通泛型类成员方法采用类类型不能使用static修饰
而泛型方法可以?
public static <E>void print(E... e){
for (int i = 0; i < e.length; i++) {
System.out.println("e = " + e[i]);
}
}
类型通配符一般是使用"?"代替具体的类型实参。
所以,类型通配符是类型实参,而不是类型形参。
?
问题
通配符解决
package com.System;
/**
* @author cqh
* @date 2024/1/8
* @Description
*/
public class BoxTst {
public static void main(String[] args) {
Box<String> stringBox = new Box<>();
stringBox.setFirst("你好");
showBox(stringBox);
Box<Integer> integerBox = new Box<>();
integerBox.setFirst(100);
showBox(integerBox);
}
public static void showBox(Box<?> box){
Object first = box.getFirst();
System.out.println("first = " + first);
}
}
要求该泛型的类型,只能是实参类型,或实参类型的子类类型。
?
?不能填充元素
?
泛型通配符下限在比较器中的应用
?在上面几个实体类中分别加入属性并实现构造方法
?
?
然后编写测试类及自定义比较器实现类进行实验发现
package com.demo2;
import java.util.Comparator;
import java.util.TreeSet;
/**
* @author cqh
* @date 2024/1/12
* @Description
*/
public class Test02 {
public static void main(String[] args) {
TreeSet<Cat> cats = new TreeSet<>(new MyComparator1());
//TreeSet<Cat> cats = new TreeSet<>(new MyComparator2());
//TreeSet<Cat> cats = new TreeSet<>(new MyComparator3());
cats.add(new Cat("jerry",21));
cats.add(new Cat("amy",13));
cats.add(new Cat("franck",27));
cats.add(new Cat("jim",18));
cats.forEach(cat -> System.out.println("cat = " + cat));
}
}
class MyComparator1 implements Comparator<Animal>{
@Override
public int compare(Animal o1, Animal o2) {
return o1.name.compareTo(o2.name);
}
}
class MyComparator2 implements Comparator<Cat>{
@Override
public int compare(Cat o1, Cat o2) {
return o1.age - o2.age;
}
}
class MyComparator3 implements Comparator<MiniCat>{
@Override
public int compare(MiniCat o1, MiniCat o2) {
return o1.level - o2.level;
}
}
第一个按照名字字母顺序排序了
第二个按照年龄从小到大排列了?
然而第三个报错了
原因是第三个比较器是使用cat 的子类minicat的level 来进行比较的但是cat中没有level所有报错了
概念
泛型是Java 1.5版本才引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前版本的代码兼容。那是因为,泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为--类型擦除。
上面虽然ArrayList分别是integer和String 类型但本质还是arraylist 并且是同一个类
?
编译运行后进行类型擦除这里是无限制类型擦除
有限制类型擦除
编译器运行后将类型转化为上限类型
擦除方法中类型定义参数
桥接方法
创建注意
可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
但可以先创建一个集合数组然后将字符数组集合指向该集合数组
但当创建一个Integer类型的集合并存入一个元素后,将该集合添加到字符集合中取得时候就会出现问题
这样是可以的
正确写法1
可以通过java.lang.reflect.Array的newInstance(Class<T>,int)创建T[]数组
创建泛型数组
完整代码
package com.demo5;
import com.System.FanXIng;
import java.lang.reflect.Array;
import java.util.ArrayList;
/**
* @author cqh
* @date 2024/1/12
* @Description
*/
public class Fruit<T> {
private T[] array;
public Fruit(Class<T> clz,int length){
array= (T[])Array.newInstance(clz,length);
}
// 填充数组元素
public void put(int index,T item){
array[index] =item;
}
// 获取数组元素
public T get(int index){
return array[index];
}
public T[] getArray(){
return array;
}
}
?
?
?