一、什么是泛型
泛型:就是指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时(创建对象)再进行类型的定义。会在编译期检查类型是否错误。
类声明后的<>中这个T被称为类型参数,用于指代任意类型,实际上这个T只是个代表,写什么都可以。表示此时的value1,value2都是在类定义时没有明确类型,只有在使用时才告知编译器类型。出于规范,类型参数用单个的大写字母来代替
T:代表任意类
E:表示Element的意思,或是异常
@Data
class people<A, B> {
A value1;
B value2;
}
@Test
void test1() {
people<String,Long> people = new people();
people.setValue1("sb");
people.setValue2(1L);
log.info(people.toString());//people(value1=sb, value2=1)
}
二、泛型方法
表示此方法是个泛型方法,有一个类型参数,不是返回值,只是告知编译器这是一个泛型的声明
@Data
@Slf4j
class people<A, B> {
A value1;
B value2;
//此处的泛型方法指的是有自己的类型参数,这里的A和类上的A不是同一个
public <A> void test(A t) {
log.info(String.valueOf(t));
}
}
@Test
void test2() {
people<String, Long> people = new people();
people.setValue1("sb");
people.setValue2(1L);
log.info(people.toString());//people(value1=sb, value2=1)
people.test(100L);
}
三、泛型接口
子类在实现接口时有两种选择,①要么继续保留泛型,②要么定义子类时明确类型
interface INew<T>{
T getName(T t);
}
class INewImpl1<T> implements INew<T>{
@Override
public T getName(T o) {
return o;
}
}
class INewImpl2 implements INew<String>{
@Override
public String getName(String o) {
return o;
}
}
四、通配符
1、通配符
<?>
一般用在方法参数,表示可以接受该类所有类型的泛型变量。
@Data
@Slf4j
class Message<T, K> {
T value1;
K Value2;
public void fun(Message<?, ?> sb) {
log.info("value1===={}", sb.getValue1());
log.info("value1===={}", sb.getValue2());
}
}
@Test
void test3() {
Message message = new Message();
message.setValue1("aaa");
message.setValue2(222);
message.fun(message);
Message message2 = new Message();
message2.setValue1(111);
message2.setValue2("bbb");
message2.fun(message);
}
2、上限通配符
<? extends 类>
表示?可以指代任何类型,但是该类型必须是后面类的子类。
可以是类本身,或者子类,除此之外其他类型都不可以。
@Data
@Slf4j
class Message2<T, K> {
T value1;
K Value2;
public void fun(Message2<? extends String, ? extends Integer> sb) {
log.info("value1===={}", sb.getValue1());
log.info("value1===={}", sb.getValue2());
}
}
@Test
void test4() {
Message2<String, Integer> message = new Message2<>();
message.setValue1("aaa");
message.setValue2(222);
message.fun(message);
Message2<Object, Object> message2 = new Message2();
message2.setValue1(111);
message2.setValue2("bbb");
message2.fun(message2);
}
3、下限通配符
<? super 类>
此时?表示可以指代任意类型,但是该类型必须是后面类的父类。
此时表示?必须是String及其父类,所有此时?只能指代String或Object。
注意:下限通配符可以调用对象的setter方法设置一个具体的属性值,无论?是什么类型,规定好的下限类型一定可以通过向上转型变为父类。
@Data
@Slf4j
class Message3<T, K> {
T value1;
K Value2;
public void fun(Message3<? super String, ? super Integer> sb) {
log.info("value1===={}", sb.getValue1());
log.info("value1===={}", sb.getValue2());
}
}
@Test
void test5() {
Message3<String, Integer> message = new Message3<>();
message.setValue1("aaa");
message.setValue2(222);
message.fun(message);
Message3<Object, Object> message2 = new Message3<>();
message2.setValue1(111);
message2.setValue2("bbb");
message2.fun(message2);
Message3<Integer, String> message3 = new Message3<>();
message3.setValue1(111);
message3.setValue2("bbb");
message3.fun(message3);
}
4、类型擦除
类型擦除:所有泛型类型参数,若没有设置泛型上限,则编译之后统一擦除为Object类型,若设置了泛型上限,则编译之后统一擦除为相应的泛型上限。
五、补充
1、泛型方法
定义泛型方法语法格式
调用泛型方法语法格式
说明一下,定义泛型方法时,必须在返回值前边加一个<T>
,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。
泛型方法要求的参数是Class<T>
类型,而Class.forName()
方法的返回值也是Class<T>
,因此可以用Class.forName()
作为参数。其中,forName()
方法中的参数是何种类型,返回的Class<T>
就是何种类型。在本例中,forName()
方法中传入的是User类的完整路径,因此返回的是Class<User>
类型的对象,因此调用泛型方法时,变量c的类型就是Class<User>
,因此泛型方法中的泛型T就被指明为User,因此变量obj的类型为User。
2、上下限补充
class A{}
class B extends A {}
// 如下两个方法不会报错
public static void funA(A a) {
// ...
}
public static void funB(B b) {
funA(b);
// ...
}
// 如下funD方法会报错
//public static void funC(List<A> listA) {
// // ...
//}
//public static void funD(List<B> listB) {
// funC(listB); // Unresolved compilation problem: The method doPrint(List<A>) in the type test is not applicable //for the arguments (List<B>)
// // ...
//}
//解决方式
public static void funC(List<? extends A> listA) {
// ...
}
public static void funD(List<B> listB) {
funC(listB); // OK
// ...
}
上限
class Info<T extends Number>{ // 此处泛型只能是数字类型
private T var ; // 定义泛型变量
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
public String toString(){ // 直接打印
return this.var.toString() ;
}
}
public class demo1{
public static void main(String args[]){
Info<Integer> i1 = new Info<Integer>() ; // 声明Integer的泛型对象
}
}
下限
class Info<T>{
private T var ; // 定义泛型变量
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
public String toString(){ // 直接打印
return this.var.toString() ;
}
}
public class GenericsDemo21{
public static void main(String args[]){
Info<String> i1 = new Info<String>() ; // 声明String的泛型对象
Info<Object> i2 = new Info<Object>() ; // 声明Object的泛型对象
i1.setVar("hello") ;
i2.setVar(new Object()) ;
fun(i1) ;
fun(i2) ;
}
public static void fun(Info<? super String> temp){ // 只能接收String或Object类型的泛型,String类的父类只有Object类
System.out.print(temp + ", ") ;
}
}
<?> 无限制通配符
<? extends E> extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类
<? super E> super 关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类
// 使用原则《Effictive Java》
// 为了获得最大限度的灵活性,要在表示 生产者或者消费者 的输入参数上使用通配符,使用的规则就是:生产者有上限、消费者有下限
1. 如果参数化类型表示一个 T 的生产者,使用 < ? extends T>;
2. 如果它表示一个 T 的消费者,就使用 < ? super T>;
3. 如果既是生产又是消费,那使用通配符就没什么意义了,因为你需要的是精确的参数类型。
3、多个限制
使用&符号
public class Client {
//工资低于2500元的上斑族并且站立的乘客车票打8折
public static <T extends Staff & Passenger> void discount(T t){
if(t.getSalary()<2500 && t.isStanding()){
System.out.println("恭喜你!您的车票打八折!");
}
}
public static void main(String[] args) {
discount(new Me());
}
}
4、泛型数组
首先,我们泛型数组相关的申明:
List<String>[] list11 = new ArrayList<String>[10]; //编译错误,非法创建
List<String>[] list12 = new ArrayList<?>[10]; //编译错误,需要强转类型
List<String>[] list13 = (List<String>[]) new ArrayList<?>[10]; //OK,但是会有警告
List<?>[] list14 = new ArrayList<String>[10]; //编译错误,非法创建
List<?>[] list15 = new ArrayList<?>[10]; //OK
List<String>[] list6 = new ArrayList[10]; //OK,但是会有警告
合理使用
public ArrayWithTypeToken(Class<T> type, int size) {
array = (T[]) Array.newInstance(type, size);
}