第一:参数类型用于类上案例:
编写泛型类GenericClass:
package com.test.cgb;
/**
*
* @Description: 定义泛型类,在类名后添加 对尖括号
* 并在尖括号中填写类型参数,参数可以有多个,多个参数使用逗号分隔
* T表示的是任意类型 type
* E表示的是元素的类型 element
* K表示的是key/value中的key
* V表示的是key/value中的value
* 通常类型参数我们都是使用大写。
* @author: caigb
* @createTime: 2020年2月29日
* @version:
*/
public class GenericClass {
public T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
编写测试类:
public class GenericDemo {
public static void main(String[] args) {
// 参数类型为String
GenericClass genericClassOfString = new GenericClass<>();
genericClassOfString.setValue("用于类上的泛型");
String str = genericClassOfString.getValue();
System.out.println("str = " + str);
// 参数类型为Integer
GenericClass genericClassOfInteger = new GenericClass<>();
genericClassOfInteger.setValue(111);
Integer intNum = genericClassOfInteger.getValue();
System.out.println("intNum = " + intNum);
}
输出结果为:
str = 用于类上的泛型
intNum = 111
==================================================================================================================================
第二个:泛型接口:
定义接口如:
/**
*
* @Description: 用于接口上的泛型
* @author: caigb28355
* @createTime: 2020年2月29日
* @version:
*/
public interface GenericInterface {
void show(T value);
}
那么针对这个接口我们可以分别定义多种类型的实现类如:
IntegerShowGenericInterfaceImpl:
package com.test.cgb;
// 泛型类型参数为Integer
public class IntegerShowGenericInterfaceImpl implements GenericInterface{
@Override
public void show(Integer value) {
System.out.println(" 整数型 "+ value);
}
}
StringShowGenericInterfaceImpl:
package com.test.cgb;
// 泛型类型参数为String
public class StringShowGenericInterfaceImpl implements GenericInterface{
@Override
public void show(String value) {
System.out.println(" 字符串 "+ value);
}
}
编写测试类如下:
public class GenericDemo {
public static void main(String[] args) {
GenericInterface IntFace = new IntegerShowGenericInterfaceImpl(); // 向上转型
IntFace.show(200);
GenericInterface StringFace = new StringShowGenericInterfaceImpl(); // 向上转型
StringFace.show("字符串");
}
// 输出结果为:
整数型 200
字符串 字符串
==================================================================================================================================
第三:泛型方法,可以定义在泛型类中,也可以定义在普通类中。如果可以定义泛型方法,那就尽量定义泛型方法
泛型方法类定义如:
package com.test.cgb;
/**
*
* @Description: 泛型方法,可以定义在泛型类中,也可以定义在普通类中
* 定义泛型方法,在返回值前边,修饰符后边添加尖括号,并在其中填写类型参数
* 如果可以定义泛型方法,那就尽量定义泛型方法
* @author: caigb
* @createTime: 2020年2月29日
* @version:
*/
public class GenericFun {
public static void saveScore(T score) {
// 这是一个保存分数的方法,分数的类型可以是int,double,float...数值类型
// 我们对分数进行复杂操作......
System.out.println("score = " + score);
}
}
编写测试类如下:
public class GenericDemo {
public static void main(String[] args) {
GenericFun.saveScore(20.0);
GenericFun.saveScore(20);
}
打印结果如下:
score = 20.0
score = 20
但是上面这个例子有个不足的就是,命名这个方法是用来保存分数的,由于使用了static形式,所以可以接收所有类型的值比如字符串中文,那么就有点违背最初的想法了,如:
public static void main(String[] args) {
GenericFun.saveScore(20.0);
GenericFun.saveScore(20);
GenericFun.saveScore("保存分数的入参竟然是中文");
}
打印结果有:
score = 20.0
score = 20
score = 保存分数的入参竟然是中文
所以为了改进上面的缺陷,我们需要限制一下参数类型,于是要对引入类型参数做一下限定,这个限定可以是类,也可以是接口,而且可以同时限定多个,如果有多个,可以使用&符号连接,如果限定中既有类又有接口,那么类一定要写在前面或者如:
public class GenericFun {
// T extends Number表示T这个类型必须继承自Number类型,也就是一定要是Number类的字类,那么也就不能是String类型了
public static void saveScore(T score) {
// 这是一个保存分数的方法,分数的类型可以是int,double,float...数值类型
// 我们对分数进行复杂操作......
System.out.println("score = " + score);
}
// Double 和Float都是继承字Number类如:
//public final class Double extends Number implements Comparable {
// /**
// * A constant holding the positive infinity of type
// * {@code double}. It is equal to the value returned by
// * {@code Double.longBitsToDouble(0x7ff0000000000000L)}.
// */
// public static final double POSITIVE_INFINITY = 1.0 / 0.0;
//}
//public final class Float extends Number implements Comparable {
// /**
// * A constant holding the positive infinity of type
// * {@code float}. It is equal to the value returned by
// * {@code Float.intBitsToFloat(0x7f800000)}.
// */
// public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
}
这样改造之后,在测试类中如果还是调用GenericFun.saveScore("保存分数的入参竟然是中文")方法的话就会报错如:
public class GenericDemo {
public static void main(String[] args) {
GenericFun.saveScore(20.0);
GenericFun.saveScore(20);
// 下面这句报错信息就是The method saveScore(T) in the type GenericFun is not applicable for the arguments (String)
GenericFun.saveScore("这里有中文");
}
===============================================================================================================================
泛型之类型擦出重要知识点扩展:下面让我们一起学习下泛型是如何擦出的,我们要知道,对于JVM来说,根本没有泛型这个概念,只有具体类型也就是说在运行期间根本就没有GenericClass,有的只是普通类和方法。JVM都会自动提供了一个相应的原始类型替换。在编译期间必要时会插入强制类型转换方法。
JVM在泛型编译时,做两件事:
1.去掉类名后的尖括号和尖括号的类型参数,剩下的名字就是对应的类名
2.对于类中使用了类型参数的变量的类型,如果没有限制类型的情况下,使用Object替换,如果有类型限定,使用限定的类型替换。下面我们一起看看:
首先我们在工作空间找到对应的class字节码文件路径,然后在该路径打开命令符窗口:
最初我们的源文件GenericClass.java代码如:
public class GenericClass {
public T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
第二步我们可以在cmd.exe窗口输入命令javap -help可以看到各种命令:
第三步我们可以输入输入命令javap -c -s GenericClass.class,我们可以看到Object类
那么如果源代码是这样的呢?就是有类型限定的如:
public class GenericClass {
public T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
那么就替换成Number了: