可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法
。
泛型字母通常类型参数都使用大写的单个字母:
T:任意类型 type
E:集合中元素的类型 element
K:key-value形式 key
V:key-value形式 value
// 泛型类
public class 类名 <泛型类型1, ...> {
}
// 泛型接口
interface 接口名 <泛型类型1, ...> {
泛型类型 方法名();
......
}
// 泛型方法
修饰符 <T, E, ...> 返回值类型 方法名() {
...
}
泛型类必须是引用类型,即类类型(不能使用基本数据类型)
。
在类名后添加一对尖括号,并在尖括号中填写类型参数。
如果参数可以有多个,多个参数使用逗号分隔。
// 定义
public class 类名 <泛型类型, ...> {
private 泛型类型 变量名;
public 泛型类型 方法名() {}
public 返回值 方法名(泛型类型 t) {}
......
}
// 使用(JDK1.7之后,结尾的具体类型可以不用写)
类名<具体数据类型> 对象名 = new 类名<>();
注意:
泛型类创建的使用没有指定类型,则默认是object类型。
泛型类型从逻辑上看是多个类型,实际都是相同类型。
Java 可以创建对应的泛型对象和泛型数组引用,但不能直接创建泛型对象和泛型数组。
Java 有类型擦除,任何泛型类型在擦除之后就变成了 Object 类型,因此创建泛型对象就相当于创建了一个 Object 类型的对象,所以直接创建泛型对象和泛型数组也的行为被编译器禁止。
泛型类中使用了泛型的方法,不能定义为static!
/**
* 栈
* 泛型类
*/
public class ArrayStack<T> {
// 声明属性
private Object[] arr;
private int top;
public ArrayStack(int capacity) {
// 这样是不可以的
// arr = new T[capacity];
arr = new Object[capacity];
top = -1;
}
/**
* 入栈
*/
public void push(T value) {
if (top == arr.length - 1) {
throw new RuntimeException("栈满");
}
arr[++top] = value;
}
/**
* 出栈
*/
public T pop() {
if (top == -1) {
throw new RuntimeException("栈空");
}
return (T) arr[top--];
}
}
public class Test {
public static void main(String[] args) {
ArrayStack<String> stack = new ArrayStack<>(10);
stack.push("测试");
// stack.push(123); // 编译出错
System.out.println(stack.pop());
// 不能用基本数据类型
// ArrayStack<int> stack2 = new ArrayStack<>(10);
// 可以这样用
ArrayStack<Integer> stack3 = new ArrayStack<>(10);
// class类型是相同的
System.out.println(stack.getClass());
System.out.println(stack3.getClass());
}
}
如果泛型类的子类也是泛型类,那父类和子类的类型要一致。
如果子类泛型有多个,那需要包括父类的泛型类型。
class Child<T, E, F> extends Parent<T> {
...
}
public class Parent<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
/**
* √ 可以子类没有泛型,父类就必须指定泛型:
* class Child extends Parent<String>
*
* √ 可以子类没有泛型,父类也没有泛型,那么默认泛型就是Object类型
* class Child extends Parent
*
* √ 子类有泛型,比如要包含了父类的:
* class Child<T> extends Parent<T>
* class Child<T, E, F> extends Parent<T>
*
* x 以下子类没有包含父类的,就会编译出错:
* class Child<E, F> extends Parent<T>
*/
class Child<T, E, F> extends Parent<T> {
public void setValue(T value) {
super.setValue(value);
}
}
interface 接口名称 <泛型类型1, ...> {
泛型类型 方法名();
...
}
规则和泛型类一样
。
如果实现类是泛型类,那接口和实现类的泛型类型要一致;如果实现类泛型有多个,那需要包括接口的泛型类型。
如果实现类不是泛型类,那接口要明确泛型类的类型。
调用方法的时候指定泛型的具体类型。
修饰符 <T, E, ...> 返回值类型 方法名(参数列表) {
方法体
}
注意,修饰符和返回值中间有<T, E...>才是泛型方法
,泛型类里面的普通返回值类型不是泛型方法。
泛型类的类型和泛型方法的类型是互相独立的,同名也不影响,同名的话泛型方法上的泛型优先。
声明了【泛型方法】在参数列表和方法体
里面才可以用对应的泛型。泛型方法可以定义为静态方法
。
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
/**
* 栈
* 泛型类
*/
public class ArrayStack<T> {
// 声明属性
private Object[] arr;
private int top;
public ArrayStack(int capacity) {
// 这样是不可以的
// arr = new T[capacity];
arr = new Object[capacity];
top = -1;
}
/**
* 入栈
*/
public void push(T value) {
if (top == arr.length - 1) {
throw new RuntimeException("栈满");
}
arr[++top] = value;
}
/**
* 出栈
*/
public T pop() {
if (top == -1) {
throw new RuntimeException("栈空");
}
return (T) arr[top--];
}
/**
* 返回一个元素
*
* 泛型方法,泛型类的类型和泛型方法的类型是互相独立的,同名也不影响,就近优先
* 声明了【泛型方法】在参数列表和方法体里面才可以用对应的泛型
*/
public <T> T getRandomElement(List<T> list) {
Random random = new Random();
return list.get(random.nextInt(list.size()));
}
public static void main(String[] args) {
// 类泛型是Integer
ArrayStack<Integer> stack = new ArrayStack<>(10);
// 方法泛型是String
List<String> list = new ArrayList<>();
list.add("111");
list.add("222");
list.add("333");
String randomElement = stack.getRandomElement(list);
System.out.println(randomElement);
}
}
我们可以看出,可变参数是不限制参数的类型的。
public static void main(String[] args) {
print(1, "test", "e1", "e2", 33);
/**
* class java.lang.Integer
* class java.lang.String
* e1
* class java.lang.String
* e2
* class java.lang.String
* 33
* class java.lang.Integer
*/
}
/**
* 支持多个泛型类型的,可变参数的泛型方法
*/
public static <E, F, K> void print(F f, K k, E... arr) {
System.out.println(f.getClass());
System.out.println(k.getClass());
for (E e : arr) {
System.out.println(e);
System.out.println(e.getClass());
}
}
Java泛型的通配符是用于解决泛型之间引用传递间的特殊语法。
//表示类型参数可以是任何类型
public class CustomCollection<?>{)
//表示类型参数必须是A或者是A的子类
public class CustomCollection<T extends A>{)
//表示类型参数必须是A或者是A的超类型
public class CustomCollection<T supers A>{}
通用类型通配符 <?>,如List<?>:
主要作用就是让泛型能够接受未知类型的数据。
可以把?看成所有泛型类型的父类,是一种真实的类型,类型通配符是实参,不是形参。
固定上边界的通配符 采用<? extends E>的形式:
使用固定上边界的通配符的泛型,只能够接受指定类及其子类类型的数据。
采用<? extends E>的形式,这里的E就是该泛型的上边界。
注意:这里虽然用的是extends关键字,却不仅限于继承了父类E的子类,也可以代指显现了接口E的类。
固定下边界的通配符,采用<? super E>的形式:
使用固定下边界的通配符的泛型,只能够接受指定类及其父类类型的数据。
采用<? super E>的形式,这里的E就是该泛型的下边界。
可以为一个泛型指定上边界或下边界,但是不能同时指定上下边界。
public class NumberCollection <T>{
private T value;
public NumberCollection(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public static void main(String[] args) {
NumberCollection<Integer> numberCollection = new NumberCollection<>(1);
NumberCollection<Long> numberCollection2 = new NumberCollection<>(2L);
NumberCollection<String> numberCollection3 = new NumberCollection<>("3");
print(numberCollection);
print(numberCollection2);
print(numberCollection3);
}
public static void print(NumberCollection<?> numberCollection) {
// 使用泛型通配符,直接获取到父类
Object value = numberCollection.getValue();
System.out.println(value);
}
}
(3)上边界
固定上边界的通配符 采用<? extends E>的形式。
使用固定上边界的通配符的泛型,只能够接受指定类及其子类类型的数据。
采用<? extends E>的形式,这里的E就是该泛型的上边界。
注意:这里虽然用的是extends关键字,却不仅限于继承了父类E的子类,也可以代指显现了接口E的类。
/**
* 只能接受Number及其子类的类型
*/
public class NumberCollection <T extends Number>{
private T value;
public NumberCollection(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public static void main(String[] args) {
NumberCollection<Integer> numberCollection = new NumberCollection<>(1);
NumberCollection<Long> numberCollection2 = new NumberCollection<>(2L);
// NumberCollection<String> numberCollection3 = new NumberCollection<>("3"); // 编译报错,String不属于Number的子类
print(numberCollection);
print(numberCollection2);
// print(numberCollection3); // 编译报错,String不属于Number的子类
}
public static void print(NumberCollection<? extends Number> numberCollection) {
// 使用泛型通配符,直接获取到父类
Number value = numberCollection.getValue();
System.out.println(value);
}
}
固定下边界的通配符,采用<? super E>
的形式。
使用固定下边界的通配符的泛型,只能够接受指定类及其父类类型的数据
。
采用<? super E>
的形式,这里的E就是该泛型的下边界。
可以为一个泛型指定上边界或下边界,但是不能同时指定上下边界。
public class NumberCollection <T>{
private T value;
public NumberCollection(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public static void main(String[] args) {
NumberCollection<Integer> numberCollection = new NumberCollection<>(1);
NumberCollection<Long> numberCollection2 = new NumberCollection<>(2L);
NumberCollection<String> numberCollection3 = new NumberCollection<>("3"); // 编译报错,String不属于Number的子类
print(numberCollection);
// print(numberCollection2); 不可以用了
// print(numberCollection3); // 编译报错,String不属于Number的子类
}
public static void print(NumberCollection<? super Integer> numberCollection) {
// 只能获取Object类型
Object value = numberCollection.getValue();
System.out.println(value);
}
}
import java.lang.reflect.Field;
// 没指定类型,则擦除后是Object类型,这是最顶级的父类
public class Generic <T extends Number, K>{
private T age;
private K name;
public static void main(String[] args) {
Generic<Integer, String> generic = new Generic<>();
// 反射获取字节码文件class对象
Class<? extends Generic> aClass = generic.getClass();
// 获取所有成员变量
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
// 获取每个属性名和类型
System.out.println(declaredField.getName() + ", type is:" + declaredField.getType().getSimpleName());
/**
* age, type is:Number
* name, type is:Object
*/
}
}
}
import java.lang.reflect.Array;
public class GenericsArray<T> {
private T[] array;
public GenericsArray(Class<T> clz)
{
// 通过反射创建数组,然后强制类型转换
this.array = (T[])Array.newInstance(clz, 10);
}
public T[] getArray() {
return array;
}
public void setArray(T[] array) {
this.array = array;
}
public static void main(String[] args) {
GenericsArray<String> genericsArray = new GenericsArray<>(String.class);
genericsArray.setArray(new String[]{"1", "2", "3"});
String[] array = genericsArray.getArray();
System.out.println(array);
}
}
public class GenericsArray2<T> {
// 通过Object来避免泛型数组的编译问题
private Object[] array;
public GenericsArray2()
{
this.array = new Object[10];
}
// 无法强转,会报错
public T[] getArray() {
return (T[])array;
}
public static void main(String[] args) {
GenericsArray2<String> genericsArray = new GenericsArray2<>();
String[] array = genericsArray.getArray();
System.out.println(array);
}
}
获取数组的时候,无法进行强转,会报错。
ArrayList会最终有个toArray的方法:
————————————————
本文系转载,版权归原作者所有,如若侵权请联系我们进行删除!
《数据资产管理白皮书》下载地址:https://www.dtstack.com/resources/1073/?src=bbs
《行业指标体系白皮书》下载地址:https://www.dtstack.com/resources/1057/?src=bbs
《数据治理行业实践白皮书》下载地址:https://www.dtstack.com/resources/1001/?src=bbs
《数栈V6.0产品白皮书》下载地址:https://www.dtstack.com/resources/1004/?src=bbs
想了解或咨询更多有关袋鼠云大数据产品、行业解决方案、客户案例的朋友,浏览袋鼠云官网:https://www.dtstack.com/?src=bbs
同时,欢迎对大数据开源项目有兴趣的同学加入「袋鼠云开源框架钉钉技术群」,交流最新开源技术信息,群号码:30537511,项目地址:https://github.com/DTStack