java泛型
泛型作用:
安全:在编译的时候检查类型安全;
省心:所有的强制转换都是自动和隐式的,提高代码的重用率;
1.格式:
class 类型<字母列表>{
修饰符 构造器(字母);
修饰符 返回类型 方法(字母)
}
泛型常见字母:
T Type 表示类型
K V 分别代表键值中的Key和Value
E 代表Element
? 表示不确定的类型
注:泛型声明时,字母不能使用在静态属性、静态方法上;
因为泛型是在使用的时候确定的,静态属性、静态方法编译的时候就确定;
2.使用
使用时指定具体的类型,(不能指定基本类型,而是用引用类型)
1)编译时会进行类型检查;
2)获取数据时不需要进行类型转换
例子1:
package com.cy.gen; /** * 泛型类:声明时使用泛型 * */ public class Student<T1, T2> { private T1 javaScore; private T2 oracleScore; //private static T1 phpScore; public T1 getJavaScore() { return javaScore; } public void setJavaScore(T1 javaScore) { this.javaScore = javaScore; } public T2 getOracleScore() { return oracleScore; } public void setOracleScore(T2 oracleScore) { this.oracleScore = oracleScore; } public static void main(String[] args) { Student<String, Integer> stu = new Student<String, Integer>(); //使用时指定类型,引用类型 stu.setJavaScore("优秀"); //类型检查 int oracleScore = stu.getOracleScore(); //自动类型转换 } }
例子2:
查看代码
package com.generic;
import java.util.List;
/**
* 类级别的泛型
*/
public class GenericDao<T> {
public void add(T t) {
}
/**
* 查单个
*/
public T findById(int id) {
return null;
}
/**
* 查集合:根据条件查询返回集合
*/
public List<T> findByConditions(String where) {
return null;
}
public void delete(T t) {
}
/**
* 注意:这里编译器报错了,不能这么写。
* 当一个变量被声明为泛型时,只能被实例变量和方法调用,而不能被静态变量和静态方法调用。
* 因为静态成员是被所有参数化的类所共享的,所以静态成员不应该有类级别的类型参数。
*
* 大白话解释:GenericDao对象里面的参数类型是T,静态方法不用创建对象就可以直接调用,对象
* 都没创建里面的参数类型怎么会知道呢?
*
* 要硬写的话只能再void前写T,但是这个T和类中的泛型T就没关系了,是方法自己的,独立的。
*/
public static void update(T t) {
}
}
例子2,泛型接口:
package com.cy.gen; public interface Comparator<T> { void compare(T t); }
例子3:泛型方法:
修饰符 <字母> 返回类型 方法名(字母){
}
要定义泛型方法,只需将泛型参数列表置返回值前;
注:泛型还可以定义在方法中,是否拥有泛型方法,与其所在的类是否泛型没有关系。
package com.cy.gen; import java.io.Closeable; import java.io.IOException; /** * 泛型方法 <> 返回类型前面 * 只能访问对象的信息,不能修改信息 */ public class TestMethod { public static void main(String[] args) { test("b"); //T --> String test(1); } //泛型方法 public static <T> void test(T t){ //t.setXXX(); System.out.println(t); } //extends <= //T是Closeable接口的实现类 ...代表可变参数 public static <T extends Closeable> void test(T... a){ for(T temp: a){ try { if(temp!=null) temp.close(); } catch (IOException e) { e.printStackTrace(); } } } }
泛型用在方法中,extends,super的例子使用:
查看代码
package com.generic;
import cn.hutool.core.collection.CollectionUtil;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class GenericTest1 {
/**
* 泛型 extends 泛型 super
* 其实解决的就是一个参数范围,向下与向上
*/
public static void testWithGenericRange() {
List<Double> list1 = new ArrayList<>();
sort(list1);
List<Double> list2 = new ArrayList<>();
list2.add(2D);
list2.add(3D);
List<Integer> list3 = new ArrayList<>();
list3.add(2);
list3.add(3);
Filter<Number> filter = o -> o.doubleValue() > 0D;
removeIf(list2, filter);
removeIf(list3, filter);
}
public static void sort(List<? extends Number> list) {
if (CollectionUtil.isEmpty(list)) {
return;
}
CollectionUtil.sort(list, Comparator.comparingInt(Number::intValue));
}
interface Filter<E> {
boolean test(E e);
}
/**
* 修饰符 <字母> 返回类型 方法名(字母)
* public static: 修饰符
* <E>:字母
* List<E>: 返回类型
* removeIf:方法名
*/
public static <E> List<E> removeIf(List<E> list, Filter<? super E> filter) {
List<E> toRemove = new ArrayList<>();
for (E e: list) {
if (filter.test(e)) {
toRemove.add(e);
}
}
list.removeAll(toRemove);
return toRemove;
}
}
有界限的泛型:
有界限的泛型写法: package com.generic;
public class GenericTest3 {
public static void main(String[] args) {
Print<Car> p1 = new Print<>(new Car());
p1.print();
Print<Bus> p2 = new Print<>(new Bus());
p2.print();
}
}
/**
* 有界限的泛型
* T extends Vehicle & Thing : 传入的参数必须是Vehicle的子类,而且实现了Thing接口
* 写法是:T extends class & interface
*/
class Print<T extends Vehicle & Thing> {
private T content;
Print(T content) {
this.content = content;
}
public void print() {
System.out.println("品牌:" + content.getBrand());
System.out.println(content);
}
}
class Vehicle implements Thing{
String brand;
String color;
@Override
public String getBrand() {
return this.brand;
}
@Override
public String getColor() {
return this.color;
}
}
class Bus extends Vehicle {
}
class Car extends Vehicle {
}
interface Thing {
String getBrand();
String getColor();
}
3.派生子类
属性类型
方法重写
泛型擦除
例子1:父类为泛型:
package com.cy.gen02; /** * 父类为泛型类 * 1.属性 * 2.方法 * * 要么同时擦除,要么子类大于等于父类的类型 */ public abstract class Father<T, T1> { T name; public abstract void test(T t); } /** * 子类声明时指定具体类型 */ class Child1 extends Father<String, Integer>{ @Override public void test(String t) { } } /** * 子类为泛型类,类型在使用时确定 */ class Child2<T, T1> extends Father<T, T1>{ @Override public void test(T t) { } } /** * 子类为泛型类,父类不指定类型,泛型的擦除,使用Object替换 */ class Child3<T1, T2> extends Father{ @Override public void test(Object t) { } }
例子2:泛型接口:
package com.cy.gen02; /** * 泛型接口: 与继承同理 * 重写方法随父类而定 * @author CY * * @param <T> */ public interface Comparable<T> { void compare(T t); } //声明子类指定具体类型 class Comp implements Comparable<String>{ @Override public void compare(String t) { } } //擦除 class Comp1 implements Comparable{ @Override public void compare(Object t) { } } //父类擦除,子类泛型 class Comp2<T> implements Comparable{ @Override public void compare(Object t) { } } //子类泛型>=父类泛型 class Comp3<T, T1> implements Comparable<T>{ @Override public void compare(T t) { } }
4.通配符
? extends super
1.可以用在声明类型、声明方法参数上,不能用在声明类上
2.?可以接受泛型的任意类型,只能接收和输出,不能修改。(方法声明时参数无法正确知道具体的类型,因此不能修改)
3.?extends 泛型上限 <= (指定类型为子类或自身)
4. ? super 泛型下限 >= (指定类型为自身或父类)
package com.cy.gen03; /**通配符 * ? 类型不定,使用时确定类型 * ?使用: 声明类型|声明方法上,不能声明类或使用时用? * * */ public class Student<T> { T score; public static void main(String[] args) { Student<?> stu = new Student<String>(); test(new Student<Integer>()); test2(new Student<Apple>()); test3(new Student<Fruit>()); } public static void test(Student<?> stu){ } // <= public static void test2(Student<? extends Fruit> stu){ } // >= public static void test3(Student<? super Apple> stu){ } } class Fruit{ } class Apple extends Fruit{ }
例2:?通配符的使用例子
package com.generic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class GenericTest2 {
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("abc");
print(list1);
List<Integer> list2 = new ArrayList<>();
list2.add(1);
print(list2);
}
/**
* ? 通配符
* 使用? 通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,
* 不能调用与参数化有关的方法
*/
public static void print(Collection<?> collection) {
/**
* 这里不能写:collection.add("abc); 在未使用之前不知道collection中是什么类型
* 不能调用一个与参数有关的方法。与参数类型有关的方法都不能调用。
* 可以写:collection.size(),因为与参数无关
*/
System.out.println("大小:" + collection.size());
System.out.println(collection);
for (Object o : collection) {
System.out.println(o);
}
}
}
5.泛型类的继承与实现
package com.generic;
import java.io.Serializable;
public class GenericTest2 {
/**
* 泛型类的继承与实现
* 继承Object
* 实现了Serializable、Runnable、Comparable接口,每个接口之间用&符号连接
*/
class TestGeneric<E extends Object & Serializable & Runnable & Comparable> {
}
}
6.擦除
java中的泛型类型(或者泛型)类似C++中的模板。但是这种相似性仅限于表面,Java语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为Java厂商升级其JVM造成难以逾越的障碍。所以,java的泛型采用了可以完全在编译器中实现的擦除方法。
所以这也就解释了为什么这样的代码编译器会报错,也不构成重载:
public void add(List<String> list) {}
public void add(List<Integer> list) {}
因为在擦除之后,最后实际上生成的编译后的class文件中,add方法中实际上传的就是List,这两个方法就重复了,所以会报错。
7.通过反射获得泛型的实际类型参数
package com.generic;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.Vector;
/**
* 通过反射获得泛型的实际类型参数
*/
public class GenericTest4 {
/**
* 很想用反射的方式来得到Vector里面装的到底是什么类型,怎么做?
*/
public static void main(String[] args) throws NoSuchMethodException {
//Vector<Date> v1 = new Vector<>();
//v1.getClass()...
Method method = GenericTest4.class.getMethod("applyVector", Vector.class);
Type[] types = method.getGenericParameterTypes();
ParameterizedType pType = (ParameterizedType) types[0];
//打印原始类型
System.out.println(pType.getRawType());
//打印实际参数类型,可能有多个,比如Map的<K, V>有两个,这里只有1个,所以取下标0
System.out.println(pType.getActualTypeArguments()[0]);
}
/**
* 没法直接通过v1.getClass()等..来获取Vector中装的是什么类型。
* 但是可以通过applyVector方法来知道它的参数列表的类型
*/
public static void applyVector(Vector<Date> v1) {
}
}
console:
class java.util.Vector
class java.util.Date
--
浙公网安备 33010602011771号