Java泛型
1. 什么是泛型?
是JDK5引起的新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型,类型安全,将本来在运行期的类型ClassCastException错误提前在编译期发现。
2. 为什么使用泛型:
- 泛型可以增强编译时错误检测,减少因类型问题引发的异常。
- 泛型可以避免类型转换
3 . 泛型算法可以增加代码复用性
3. 泛型的类型参数命名约定
按照约定,类型参数名称命名为单个大写字母,以便可以在使用普通类或接口名称时能够容易地区分类型参数。常用的类型参数名称列表如下:
E - 元素,主要由Java集合(Collections)框架使用。
K - 键,主要用于表示映射中的键的参数类型。
V - 值,主要用于表示映射中的值的参数类型。
N - 数字,主要用于表示数字。
T - 类型,主要用于表示第一类通用型参数。
S - 类型,主要用于表示第二类通用类型参数。
U - 类型,主要用于表示第三类通用类型参数。
V - 类型,主要用于表示第四个通用类型参数。
测试用例一:
/**
* 不使用泛型需要强制类型转换
* result :
* list.get(0) : class java.lang.String
* s : class java.lang.String
*/
private static void demo1(){
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);//向下转型,不转将会报错
System.out.println(" list.get(0) :" + list.get(0).getClass());
System.out.println(" s :" + s.getClass());
}
/**
* 使用泛型不需要强制类型转换
* result :
* demo2 list.get(0) :class java.lang.String
* demo2 s :class java.lang.String
*/
private static void demo2(){
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0);//不需要类型转换
System.out.println("demo2 list.get(0) :" + list.get(0).getClass());
System.out.println("demo2 s :" + s.getClass());
}
泛型的类型写法:
/**
* 泛型类(泛型接口也是这样定义)
* class name<T1, T2 ,T3>{}
*
* @param <T1>
*/
public class Box<T1> {
private T1 t;
/**
* 泛型接口
* @param <T>
*/
private interface Generics<T>{}
/**
* 泛型方法,没有返回值的
* @param t2
* @param <T2>
*/
public <T2> void test(T2 t2){
}
// 这个也不是泛型泛型
public void test2(List<String> list){
}
// 这个也不是泛型泛型
public void test3(List<?> list){
}
//注意这个不是泛型方法,就是一个泛型的入参
public void set(T1 t) {
this.t = t;
}
//注意这个不是泛型方法
public T1 get() {
return t;
}
}
下面是要注意的泛型的接口实现:
interface Generics<T> {}
//1. 泛型类/泛型接口 继承/实现
class Generics2<T> implements Generics<T>{}
//2. 泛型类/泛型接口 继承/实现
class Generics3 implements Generics<String>{}
受限的类型参数:
功能:对泛型变量的范围作出限制
格式:
单一限制 :
多种限制: <U extends A &B &C>
extends 表达的意义:兼有“类继承”和接口实现的意思。
////inspect
public static <U extends Number> void inspect(U u){
System.out.println("U :" + u.getClass().getName());
}
private static void demo3(){
inspect(10L);
inspect("111");//报错
}
多重限定:
static class A {}
static interface B {}
static interface C {}
//只能继承一个实例类A 接口可以继承多个
static class D1<T extends A & B & C> {}
受限类型的使用案例,这种因为方法里面使用了compareTo,如果不将T限定为Comparable,
而是让T继承一个没有实现compareTo方法的类,函数将报错:
public static <T extends Comparable<T>> int countGreater(T[] anArray, T elem) {
int count = 0;
for (T e : anArray) {
//受限于Comparable<T>,所以可以使用compareTo方法
if (e.compareTo(elem) > 0) {
++count;
}
}
return count;
}
使用上限通配符:
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,1,9);
double s = sumofList(list);
System.out.println(" upper_bounded s :" + s);
List<Double> listDouble = Arrays.asList(1.1,2.2,3.5,1.8,9.4);
double sumDouble = sumofList(listDouble);
System.out.println(" upper_bounded sumDouble :" + sumDouble);
//List<String> listString = Arrays.asList("a","b","c"); 报错
//sumofList(listString);
}
// 表示list里面元素的集合只能是Number及其子类,使用的是上限通配符,就是Producer extends
public static double sumofList(List<? extends Number> list){
double sum = 0f;
//list.add(2); 副作用 ,只能读不能往里面添加
for (Number n: list) {
sum += n.doubleValue();
}
return sum;
}
泛型的约束
1.不能实例化对象。
2.静态域或者方法里面不能引用类型变量,至于为什么不能引用要看static变量的创建时机
//静态域或者方法里不能引用类型变量,虚拟机在创建对象的时候,先执行static
// 再构执行构造方法
//构造方法都没有
private static T instance;
private static T getInstance(){}
//但是可以使用,这个T和前面的类型T可以完全没有关系
private static <T> T getInstance(){}
public class Restrict<T> {
private T data;
public static void main(String[] args) {
Restrict<Double> restrict = new Restrict<>();
Restrict<String> restrictString = new Restrict<>();
//true
System.out.println(restrict.getClass() == restrictString.getClass());
//class com.genericsdemo.why_use_generics_01.Restrict
System.out.println(restrict.getClass());
}
}
不能创建泛型数组
Restrict<Double>[] list = new Restrict<Double> [10];
public class Restrict {
//泛型类不能继承Exception
private class problem<T> extends Exception{}
//这种继承是可以的
private class problem1<T> extends Restrict{}
}
//不能捕获泛型类对象
private <T extends Throwable> void dowork(){
try {
}catch (T t){}
}
泛型通配符的使用
只能在使用方法的时候使用,向上限定符extends 为了安全的读取
向下限定符super 为了安全的写入
在Javac编译之后可以看到去掉了泛型,义原生类型-fu来替换 差除替换泛型
public void fun(List<? extends Fruit> arraylist){
}