一、基本介绍
Java泛型是java1.5引入的一个新特性,其本质是参数化类型,也就是说所操作的数据类型被指定一个参数,这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
二、提出背景
在Java1.5之前构建一个元素类型为Object的Collection.其中里面的元素可以是任意类型的数据,在没有泛型的情况下,通过对类型Object的引用来实现参数的任意化,任意化带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以在预知的情况下进行的。如果是其他地方调用的情况,因为对实际参数类型不知情,就会出现强制类型转换的错误。因此为了解决这一问题,java1.5之后就引入了泛型。
package day01;
import java.util.ArrayList;
import java.util.List;
/**
* @author qx
* @date 2023/10/20
* @des 没有泛型产生问题
*/
public class GenericsTest {
public static void main(String[] args) {
List list = new ArrayList<>();
list.add("hello");
list.add(2);
System.out.println(list);
for (Object o : list) {
String str = (String) o;
System.out.println(str);
}
}
}
输出:
hello
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at day01.GenericsTest.main(GenericsTest.java:18)
使用泛型:
三、泛型的优缺点
优点:
类型安全:泛型的主要目的是提高Java程序的类型安全。通过知道使用泛型定义变量的类型限制,编译器可以及时处理类型的验证。
消除强制性类型转换:泛型可以消除源代码中许多强制性类型转换。
更高的效率:在泛型出现之前我们都是用Object类型作为参数类型操作,这就会引起频繁的装箱和拆箱操作,这两个过程都具有很大开销。引入泛型后就不必频繁的装箱和拆箱操作了。
四、泛型的使用
泛型有三种使用方式,分别是泛型类、泛型接口、泛型方法
1.泛型类
package day01;
/**
* @author qx
* @date 2023/10/20
* @des 泛型类
*/
public class Generic<T> {
// 成员变量的类型为T 由外部指定
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
package day01;
/**
* @author qx
* @date 2023/10/20
* @des 泛型类测试
*/
public class GenericTest {
public static void main(String[] args) {
Generic<Integer> generic = new Generic<>();
generic.setData(20);
// 输出20
System.out.println(generic.getData());
}
}
2.泛型接口
package day01;
/**
* @author qx
* @date 2023/10/20
* @des 泛型接口
*/
public interface Generator<T> {
T show(T data);
}
两个泛型接口实现类:
package day01;
/**
* @author qx
* @date 2023/10/20
* @des 泛型接口实现类
*/
public class NameGenerator implements Generator<String> {
@Override
public String show(String data) {
System.out.println("my name is " + data);
return data;
}
}
package day01;
/**
* @author qx
* @date 2023/10/20
* @des 泛型接口实现类
*/
public class AgeGenerator implements Generator<Integer> {
@Override
public Integer show(Integer data) {
System.out.println("my age is " + data);
return data;
}
}
package day01;
/**
* @author qx
* @date 2023/10/20
* @des 泛型接口测试
*/
public class GeneratorTest {
public static void main(String[] args) {
Generator<String> generator = new NameGenerator();
generator.show("hello");
}
}
3.泛型方法
泛型方法是在调用方法的时候指明泛型的具体类型。
package day01;
/**
* @author qx
* @date 2023/10/20
* @des 泛型方法测试
*/
public class GenericMethodTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Student student = genericeMethod(Student.class);
System.out.println(student);
User user = genericeMethod(User.class);
System.out.println(user);
}
/**
* 泛型方法
* 只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
* <T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
*
* @param tClass
* @param <T>
* @return
* @throws InstantiationException
* @throws IllegalAccessException
*/
private static <T> T genericeMethod(Class<T> tClass) throws InstantiationException, IllegalAccessException {
return tClass.newInstance();
}
}
执行结果:
Student{id=null, name='null'}
User{id=null, name='null', password='null'}
五、泛型的上下边界
1.设定通配符的上限
我们想要接收一个数字类型的元素的集合,只要是属于数字类型的元素都可以添加到这个集合中,比如Integer,Float,Double等。这个时候我们就可以设定通配符的上限,让它是Number类或Number类的子类。
package day01;
import java.util.ArrayList;
import java.util.List;
/**
* @author qx
* @date 2023/10/20
* @des 泛型通配符上限测试
*/
public class GenericMethodTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
List<Integer> list = new ArrayList<>();
list.add(1);
test(list);
List<Double> doubleList = new ArrayList<>();
doubleList.add(3.4);
test(doubleList);
}
private static <T> void test(List<? extends Number> list) {
System.out.println(list);
}
}
输出:
[1]
[3.4]
2.设定通配符下限
语法:
//传递进来的只能是Type或Type的父类
<? super Type>
package day01;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @author qx
* @date 2023/10/20
* @des 泛型通配符下限测试
*/
public class GenericMethodTest {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
List<String> list = new ArrayList<>();
list.add("aa");
test(list);
List<Object> doubleList = new ArrayList<>();
doubleList.add(3.4);
test(doubleList);
}
private static <T> void test(List<? super String> list) {
System.out.println(list);
}
}