多平台统一管理软件接口,如何实现多平台统一管理软件接口
244
2022-12-05
深入了解JAVA泛型
什么是泛型
泛型的概念:java泛型(generics)是JDK1.5中引入的一个新特性,泛型提供了编译时的类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据结构。
泛型的本质就是类型参数化,也就是所操作的数据类型被指定为一个参数。
使用泛型的好处:
1 在编译期间提供了类型检查
2 取数据时无须进行类型装换
泛型类、接口
泛型类
语法:
class 类名称 <泛型标识,泛型标识,泛型标识,...> {
private 泛型标识 变量名;
// ...
}
常用的泛型标识:T、E、K、V
使用语法:
类名 <具体的数据类型> 对象名 = new 类名<具体的数据类型>();
JDK 1.7 之后,后面的 <> 中的具体的数据类型可以省略不写。
定义一个简单的泛型类:
/**
* 泛型类 T:类型形参,在类创建对象时,指定具体的数据类型
* @author rainszj
* 2020/3/19
*/
public class GenericDemo01
private T value;
public GenericDemo01() {
}
public GenericDemo01(T value) {
this.value = value;
}
@Override
public String toString() {
return "GenericDemo01{" +
"value=" + value +
'}';
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
测试一下:
public class Test {
public static void main(String[] args) {
// 在创建对象时指定具体的数据类型
GenericDemo01
// 泛型类不支持基本数据类型,但可以使用基本类型对应的包装类
GenericDemo01
// 在泛型类对象时,不指定具体的数据类型,将会使用Object类型来接收
// 同一个泛型类,根据不同数据类型创建的对象,本质上是同一类型,公用同一个类模板
// class com.rainszj.GenericDemo01
System.out.println(genericDemo01.getClass());
// class com.rainszj.GenericDemo01
System.out.println(genericDemo02.getClass());
// true
System.out.println(genericDemo01.getClass() == genericDemo02.getClass());
}
}
注意事项:
泛型类,如果没有指定具体的数据类型,按Object类型来接收
泛型的类型参数只能是类类型,也就是引用数据类型,不能是基本数据类型
泛型类型在逻辑上可以看成是多个不同的类型,但实际上都是相同类型
/**
* 抽奖池
*
* @author rainszj
* 2020/3/19
*/
public class ProductGetter
// 奖品
private T product;
private ArrayList
/**
* 添加奖品
*
* @param product
*/
public void addProduct(T product) {
list.add(product);
}
/**
* 抽取随机奖品
*
* @return
*/
public T getProduct() {
return list.get(new Random().nextInt(list.size()));
}
@Override
public String toString() {
return "ProductGetter{" +
"product=" + product +
'}';
}
}
public static void main(String[] args) {
ProductGetter
// 奖品类型 礼物
String[] products1 = {"华为手机", "苹果手机", "扫地机器人", "微波炉"};
// 添加奖品
for (int i = 0, length = products1.length; i < length; i++) {
productGetter1.addProduct(products1[i]);
}
// 获取奖品
String product1 = productGetter1.getProduct();
System.out.println("恭喜您抽中了," + product1.toString());
ProductGetter
// 奖品类型 money
Integer[] products2 = {1000, 3000, 10000, 500};
for (Integer money : products2) {
productGetter2.addProduct(money);
}
Integer product2 = productGetter2.getProduct();
System.out.println("恭喜您抽中了," + product2.toString());
}
从泛型类派生子类
子类也是泛型类,子类的泛型标识 T 要和父类的泛型标识 T 保持一致,或者是包含关系,子类的泛型标识包含父类的泛型标识
class ChildGeneric
class ChildGeneric
子类不是泛型类,父类要明确泛型的数据类型
class ChildGeneric extends ParentGeneric
泛型接口
语法:
interface 接口名称 <泛型标识,泛型标识,...> {
泛型标识 方法名();
}
实现泛型接口的类,不是泛型类,需要明确实现泛型接口的数据类型
public class Apple implements Generic
实现类也是泛型类,实现类和接口的泛型类型要一致,或者是包含关系,实现类的泛型标识包含泛型接口的泛型标识
public class Apple
public class Apple
定义一个泛型接口
public interface Generic
K getKey();
}
实现其中方法:
/**
* 泛型接口的实现类,是一个泛型类,
* 那么要保证实现接口的泛型类的泛型标识包含泛型接口的泛型标识
*/
public class Pair
private K key;
private V value;
public Pair() {
}
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
public V getValue() {
return value;
}
@Override
public String toString() {
return "Pair{" +
"key=" + key +
", value=" + value +
'}';
}
}
测试:
public class MyTest {
public static void main(String[] args) {
Pair
System.out.println(pair.toString());
// Pair{key=数学, value=100}
}
}
泛型方法
普通泛型方法
泛型类,是在实例化类时指明泛型的具体类型。
泛型方法,是在调用方法时,指明泛型的具体类型。
语法:
修饰符
// 方法体...
}
public 与返回值中间
只有声明了
public class ProductSetter
private T product;
private Random random= new Random();
private ArrayList
public void addProduct(T product) {
list.add(product);
}
/**
* @param list
* @param
* @return
*/
public
return list.get(random.nextInt(list.size()));
}
public T getProduct() {
return list.get(random.nextInt(list.size()));
}
@Override
public String toString() {
return "ProductSetter{" +
"product=" + product +
'}';
}
}
测试:
public static void main(String[] args) {
ProductSetter
String[] products1 = {"华为手机", "苹果手机", "扫地机器人", "微波炉"};
for (int i = 0; i < products1.length; i++) {
productSetter.addProduct(products1[i]);
}
System.out.println(productSetter.getProduct());
ArrayList
list1.add("华硕电脑");
list1.add("苹果电脑");
list1.add("华为电脑");
String product1 = productSetter.getProduct(list1);
System.out.println(product1 + "\t" + product1.getClass().getSimpleName());
// 华为电脑 String
ArrayList
list2.add(1);
list2.add(2);
list2.add(3);
Integer product2 = productSetter.getProduct(list2);
System.out.println(product2 + "\t" + product2.getClass().getSimpleName());
// 2 Integer
}
静态泛型方法
public static
System.out.println(k1 + "\t" + k1.getClass().getSimpleName());
System.out.println(k2 + "\t" + k2.getClass().getSimpleName());
System.out.println(k3 + "\t" + k3.getClass().getSimpleName());
}
// 方法的调用
ProductSetter.pringType(1, "hello", false);
// 输出结果
1 Integer
hello String
false Boolean
注意:
// 在http://泛型类中无法添加静态的 带有泛型成员方法,但可以添加静态的 泛型方法
public class Test
// 带有泛型的成员方法
// 错误
public static T getKey(T key) {
return key;
}
// 泛型方法
// 正确
public static
return key;
}
}
泛型方法中的可变参数
public class MyTest {
public static void main(String[] args) {
MyTest.print(1, 2, 3);
}
/**
* 泛型方法中的可变长参数
* @param value
* @param
*/
public static
for (int i = 0; i < value.length; i++) {
System.out.println(value[i]);
}
}
}
总结:
泛型方法能使方法独立于类而产生变化。
如果 static 方法要使用泛型能力,就必须使其成为泛型方法。
类型通配符
类型通配符一般是使用 ? 代替具体的类型实参。
类型通配符是类型实参,而不是类型形参。
我们先来定义一个简单的泛型类:
public class Box
private T width;
public static void showBox(Box
Number width = box.getWidth();
System.out.println(width);
}
public T getWidth() {
return width;
}
public void setWidth(T width) {
this.width = width;
}
}
main方法:
public static void main(String[] args) {
Box
box1.setWidth(100);
showBox(box1);
}
当我们在 main 方法中增加这一段代码时,就会报错
Box
box2.setWidth(200);
showBox(box2);
虽然 Integer 类继承自 Number 类,但在类型通配符中不存在继承这一概念!
也许你会使用方法的重载,但是 在同一个泛型类中,根据不同数据类型创建的对象,本质上是同一类型,公用同一个类模板,所以无法通过方法的重载,传递不同的泛型类型。
这时可以使用类型通配符 ?,来代表具体的类型实参!
public static void showBox(Box> box) {
Object width = box.getWidth();
System.out.println(width);
}
类型通配符的上限
在我们上面的showBox()代码中,发现 box.getWidth()得到的还是Object类型,这和我们不使用类型通配符,得到的结果是一样的。这时我们可以使用类型通配符的上限。
语法:
类/接口 entends 实参类型>
要求该泛型的类型,只能是实参类型,或者是实参类型的子类类型。
public static void showBox(Box extends Number> box) {
Number width = box.getWidth();
System.out.println(width);
}
public static void main(String[] args) {
Box
box2.setWidth(200);
showBox(box2);
}
使用类型通配符的下限,无法得知该类型具体是指定的类型,还是该类型的子类类型,因此无法在 List 集合中执行添加该类或者该类子类的操作!
public static void showAnimal(List extends Cat> list) {
// 错误
list.add(new Cat());
list.add(new MiniCat());
}
类型通配符的下限
语法
类/接口 super 实参类型>
要求该泛型的类型,只能是实参类型,或者是实参类型的父类类型。
下面通过 TreeSet 集合中的一个构造方法来进一步理解 类型通配符的下限
public TreeSet(Comparator super E> comparator) {
this(new TreeMap<>(comparator));
}
首先是一个Animal类,只有一个 name 属性
public class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" +
"name='" + name + '\'' +
'}';
}
}
然后它的一个子类,Cat添加一个属性:age
public class Cat extends Animal {
private int age;
public Cat(String name, int age) {
super(name);
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"age=" + age +
'}';
}
}
最后是 Cat 的子类,MiniCat,再添加一个属性 level
public class MiniCat extends Cat {
private int level;
public MiniCat(String name, int age, int level) {
super(name, age);
this.level = level;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
@Override
public String toString() {
return "MiniCat{" +
"level=" + level +
'}';
}
}
测试,首先我们要在MyTest类通过静态内部类的方式,实现比较的接口,在构造TreeSet时,传递比较器
public class MyTest {
public static void main(String[] args) {
// 正常
// TreeSet
// 正常
TreeSet
// 报错
// TreeSet
List
animals.addAll(list);
animals.forEach(System.out::println);
}
public static class Comparator1 implements Comparator
@Override
public int compare(Animal o1, Animal o2) {
return o1.getName().compareTo(o2.getName());
}
}
public static class Comparator2 implements Comparator
@Override
public int compare(Cat o1, Cat o2) {
return o1.getAge() - o2.getAge();
}
}
public static class Comparator3 implements Comparator
@Override
public int compare(MiniCat o1, MiniCat o2) {
return o1.getLevel() - o2.getLevel();
}
}
}
结论:
通过以上的比较,我们可以看出,类型通配符的下限,只能传递实参类型的或者实参类型的父类类型。
我们每次比较使用的都是 Cat 类型,但在 Comparator1比较的是 Animal 中的 name 属性,这是因为 我们在初始化 Cat 对象的时候,一定会先初始化 Animal 对象,也就是创建子类对象的时候,一定会先创建父类对象,所以才可以进行比较。
如果是使用 类型通配符的上限,在创建对象时,比较的是该类的子类对象中的属性,就会造成http://空指针异常!也就是Comparator3无法使用的原因, 所以在 TreeSet 中才会使用 super E> ,类型通配符的下限。
类型擦除
泛型是Java 1.5 引进的概念,在这之前是没有泛型的,但是,泛型代码能够很好地和之前的代码兼容。那是因为,泛型信息只存在编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,我们称之为——类型擦除。
无限类型擦除
先定义一个泛型类:
public class Erasure
private T key;
public T getKey() {
return key;
}
public void setKey(T key) {
this.key = key;
}
}
输出结构:
public static void main(String[] args) {
Erasure
Class extends Erasure> cls = erasure.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName() + ":" + field.getType().getSimpleName()); // key:Object
}
}
可以发现在编译完成后的字节码文件中,T --> Object 类型
有限类型擦除
还是刚才的泛型类,只不过加了泛型的上限
public class Erasure
测试不变,输出结果:
key:Number
当我们指定了泛型的上限时,它会将我们的泛型擦除为上限类型
同样对泛型方法,也是一样的道理
// 泛型方法
public
return t;
}
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
}
// 输出结果
// getKey:Number
// test:List
// setKey:void
桥接方法
泛型接口
public interface Info
T test(T value);
}
泛型接口的实现类
public class InfoImpl implements Info
@Override
public Integer test(Integer value) {
return value;
}
}
测试
public static void main(String[] args) {
Class cls = InfoImpl.class;
Method[] methods = cls.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName() + ":" + method.getReturnType().getSimpleName());
}
}
// 输出结果:
// test:Integer
// test:Object
原本 InfoImpl 中只是实现了 Info 接口中的一个方法,但通过反射却拿到了两个方法。其中返回值为 Object 的方法就是桥接方法。
在编译完成后,类型擦除的结果是这样的:
public interface Info {
Object test(Object value);
}
public class InfoImpl implements Info {
public Integer test(Integer value) {
return value;
}
// 桥接方法:保持接口和类的实现关系
@Override
public Object test(Object value) {
return (Integer)value;
}
}
泛型数组
开发中,一般常用的是泛型集合
泛型数组的创建:
可以声明带泛型的数组引用,但是不能直接创建带泛型数组对象。
可以通过 java.lang.reflect.Array 的 newInstance(Class
// 可以创建带泛型的数组引用
ArrayList
// 无法创建带泛型的数组对象
ArrayList
简单使用 java.lang.reflect.Array 的 newInstance(Class
public class GenericArray
private T[] array;
public GenericArray(Class cls, int length) {
this.array = (T[]) Array.newInstance(cls, length);
}
public void put(int index, T item) {
this.array[index] = item;
}
public T get(int index) {
return this.array[index];
}
public T[] getArray() {
return this.array;
}
public static void main(String[] args) {
GenericArray
ga.put(0, "白虎");
ga.put(1, "青龙");
ga.put(2, "朱雀");
System.out.println(Arrays.toString(ga.getArray()));
}
}
泛型和反射
反射常用的泛型类:
Class
Constructor
通过反射创建对象,带泛型和不带泛型
Class
try {
Constructor
Cat cat = c1.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
Class catClass2 = Cat.class;
try {
Constructor c2 = catClass2.getConstructor();
Object cat2 = c2.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
以上就是深入了解JAVA泛型的详细内容,更多关于JAVA泛型的资料请关注我们其它相关文章!
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~