一、什么是泛型?泛型可以做什么?
1、Java 泛型(generics)是JDK5中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
2、通俗的来讲,就是不同的数据类型要做一个相同的操作,比如排序,求最大值等。用泛型可以用一个相同方法对代替。
3、案例
package com.hao.base;
/**
* @author haoxiansheng
* Generics 泛型类
* T - 这是参数传递给泛型类的泛型类型,可以取任何对象
* t - 泛型类型T的一个实例
*/
public class Generics<T> {
private T t;
public Generics(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
@Override
public String toString() {
return "Generics{" +
"t=" + t +
'}';
}
public void print() {
System.out.println(t);
}
}
// 测试
package com.hao.base;
/**
* @author haoxiansheng
*/
public class Test1 {
public static void main(String[] args) {
Generics<Integer> integerGenerics = new Generics<>(9);
integerGenerics.print();
Generics<String> stringGenerics = new Generics<>("hello");
stringGenerics.print();
}
}
二、泛型定义规则
1、所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前。
2、每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
3、类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
4、泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是基本类型(像int,double,char的等)。
5、命名规约
E - 元素,主要由Java集合(Collections)框架使用。
K - 键,主要用于表示映射中的键的参数类型。
V - 值,主要用于表示映射中的值的参数类型。
N - 数字,主要用于表示数字。
T - 类型,主要用于表示第一类通用型参数。
S - 类型,主要用于表示第二类通用类型参数。
U - 类型,主要用于表示第三类通用类型参数。
V - 类型,主要用于表示第四个通用类型参数。
6、例子
package com.hao.base;
import java.util.*;
/**
* @author haoxiansheng
*/
public class GenericsTest1 {
public static <E> void printArray(E[] inputArray) {
for (E e : inputArray) {
System.out.println(e);
}
}
public static <E> void printArray(List<E> inputs) {
inputs.forEach(System.out::println);
}
public static <K, V> void printMap(Map<K, V> map) {
map.forEach((k, v) -> {
System.out.println(k);
System.out.println(v);
});
}
/**
* 比较大小
* @param x
* @param y
* @param z
* @param <T>
* @return
*/
public static <T extends Comparable<T>> T maximun(T x, T y, T z) {
T max = x; // 假设x最大
if (y.compareTo(max) > 0) {
max = y;
}
if (z.compareTo(max) > 0) {
max = z;
}
return max;
}
/**
* 通配符 任何类型都可以
* @param data
*/
public static void printData(List<?> data) {
data.forEach(System.out::println);
}
static class GenericsDemo<T, S> {
private T t;
private S s;
public void add(T t, S s) {
this.s = s;
this.t = t;
}
@Override
public String toString() {
return "GenericsDemo{" +
"t=" + t +
", s=" + s +
'}';
}
}
public static void main(String[] args) {
int[] a = {1, 2, 3};
Integer[] b = {1, 2, 3};
Map<String, Integer> map = new HashMap<>();
map.put("1", 2);
// error
//printArray(a); // 不能使用基本数据类型
printArray(b);
System.out.println("=========");
printMap(map);
System.out.println("====通配符测试=====");
List<String> strings = new ArrayList<>();
strings.add("a");
strings.add("b");
printData(strings);
ArrayList<Integer> integers = new ArrayList<>();
printData(integers);
}
}
三、类型推断
1、类型推断表示Java编译器查看方法调用及其对应的声明,以检查和确定类型参数。推断算法检查参数的类型,如果可用,则返回分配的类型。
Generics<T> generics = new Generics<>();
<>:尖括号运算符表示类型推断
四、类型擦除
1、泛型在编译时用于更严格的类型检查,并提供泛型编程。 要实现通用行为,java编译器应用类型擦除。
2、类型擦除是指编译器使用实际的类或桥接方法替换泛型参数的过程。 在类型擦除中,编译器确保不会创建额外的类,并且没有运行时开销。
类型擦除规则
用通用类型的类型参数替换其绑定的有界类型参数。
如果使用无界类型参数,则使用Object替换类型参数。
插入类型转换以保护类型安全。
生成桥接方法以在扩展通用类型中保持多态。
五、泛型语法
1、语法一
package com.hao.base;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author haoxiansheng
*/
public class GenericsTest2 {
public static void main(String[] args) {
//String maximun1 = printNumber("1"); // 错误
Integer maximun = printNumber(1);
}
/**
* 声明有界泛型参数
*
* @param t
* @param <T>
* @return
*/
public static <T extends Number> T printNumber(T t) {
System.out.println(t);
return t;
}
/**
* 类型参数可以有多个边界
* @param x
* @param y
* @param z
* @param <T>
* @return
*/
public static <T extends Number & Comparable<T>> T maximun(T x, T y, T z) {
T max = x;
if (y.compareTo(max) > 0) {
max = y;
}
if (z.compareTo(max) > 0) {
max = z;
}
return max;
}
// list 泛型
public <E> void printListGenerics(List<E> generics) {
generics.forEach(System.out::println);
}
// set 泛型
public <E> void printSetGenerics(Set<E> generics) {
generics.forEach(System.out::println);
}
// map 泛型
public <K,V> void printMapGenerics(Map<K,V> generics) {
generics.forEach((k, v) ->{
System.out.println(k);
System.out.println(v);
});
}
// 上限通配符 使用带有通配符的extends关键字
public void printExtendsGenerics(Set<? extends Comparable> generics) {
generics.forEach(System.out::println);
}
// 下界通配符 使用带有通配符的super关键字
public <E> void printSuperGenerics(Set<? super GenericsTest2> generics) {
generics.forEach(System.out::println);
}
// 无界通配符 使用未绑定的通配符 可以使用Object类方法访问变量
public <E> void printObjectGenerics(Set<?> generics) {
generics.forEach(System.out::println);
}
}
2、语法二
package com.hao.base;
import java.util.ArrayList;
import java.util.List;
/**
* @author haoxiansheng
*/
public class GenericsTest3 {
public static void main(String[] args) {
/**
* 1
* 错误操作 泛型不能使用原始类型
*/
// IntTest<int> intTest = new IntTest<int>();
IntTest<String> stringIntTest = new IntTest<>();
IntTest<Integer> integerIntTest = new IntTest<>();
printIntTest(stringIntTest);
printIntTest(integerIntTest);
/**
* 2
* 类型转换错误 泛型不能转换类型
*/
IntTest<Number> number = new IntTest<>();
number.setT(7);
IntTest<Integer> integer = new IntTest<>();
//integer = (IntTest<Integer>) number;
integer = transform(number);
System.out.println(integer);
/**
* 5
* 泛型不能使用instanceof运算符
*
* 编译器使用类型擦除,运行时不会跟踪类型参数,IntTest <Integer>和IntTest <String>之间的运行时差异无法使用instanceOf运算符进行验证。
*/
// if(integerIntTest instanceof IntTest<Integer>)
/**
* 6
* 不允许使用参数化类型的数组
*
* 因为编译器使用类型擦除,类型参数被替换为Object,可以向数组添加任何类型的对象。但在运行时,代码将无法抛出ArrayStoreException。
*/
}
private static void printIntTest(IntTest intTest) {
System.out.println(intTest);
}
private static IntTest<Integer> transform(IntTest<?> intTest) {
IntTest<Integer> integer = (IntTest<Integer>) intTest;
return integer;
}
/**
* 3
* 类型参数不能用于在方法中实例化其对象
*
* @param e
* @param <E>
* @return
*/
public static <E> List<E> add(IntTest<E> e) {
List<E> list = new ArrayList<>();
// 错误的操作
//E e = new E();
//list.add(e);
return list;
}
/**
* 需要利用反射机制实现
*
* @param intTest
* @param clazz
* @param <E>
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static <E> void add(IntTest<E> intTest, Class<E> clazz)
throws IllegalAccessException, InstantiationException {
List<IntTest<E>> list = new ArrayList<>();
E e = clazz.newInstance();
intTest.setT(e);
list.add(intTest);
}
static class IntTest<T> {
/**
* 4
* 使用泛型时,类型参数不允许为静态(static)。
* 由于静态变量在对象之间共享,因此编译器无法确定要使用的类型。
*/
//private static T t;
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
@Override
public String toString() {
return "IntTest{" +
"t=" + t +
'}';
}
public void printInt() {
System.out.println(t);
}
}
/**
* 7
* 泛型不能使用异常
* 通用类不允许直接或间接扩展Throwable类。
*/
// class Box<T> extends Throwable{
//
// }
class Box<T extends Throwable> {
/**
* 9
* 一个类不允许有两个重载方法,可以在类型擦除后使用相同的签名
*/
//public void print(List<String> stringList) { }
//public void print(List<Integer> integerList) { }
}
/**
* 8
*
* 在一个方法中,不允许捕获一个类型参数的实例
*/
}