java基础----泛型程序设计

本文详细介绍了Java泛型程序设计的概念、使用方法及注意事项,包括泛型类、泛型方法、类型变量限定、通配符类型、泛型代码与虚拟机之间的交互等关键点。同时讨论了泛型与反射的结合应用,以及泛型的约束与局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

泛型程序设计

认识泛型;

在以前,ArrayList中维护一个Object数组

publicclassArrayList {

privateObject[]elementData;

...

publicObject get(inti){...}

publicvoidadd(Object o){}

}

ArrayListfiles = new ArrayList();

。。。

Stringfilename = (String)files.get(0);

如果这样,必须要强制转换,如果类型不对,会报类型转换异常


java引进了泛型ArrayList<String>有效的弥补了这个问题


简单的泛型类;


publicclass Pair<T>

publicclass Pair<T,U>

说明:

T为类型变量,泛型类可以有多个类型变量,类定义中的类型变量指方法的返回类型以及域和局部变量的类型。

类型变量使用大写,java库中,使用变量E表示集合的元素类型,KV分别表示表的关键字与值的类型,T(需要时还可以用临近的字母US)表示“任意类型”,


泛型方法;

classArrayAlg{

publicstatic <T> T getMiddle(T...a){

returna[a.length / 2]

}


}

说明:

在泛型方法中<>中的T是指这个方法的返回类型

调用这个方法:

Stringmiddle = ArrayAlg.<String>getMiddle(“john”,”Q”,”Public”)

等同于

Stringmiddle = ArrayAlg.getMiddle(“john”,”Q”,”Public”)

泛型方法可以定义在普通类中,也可以定义在泛型类中。


类型变量的限定;

publicstatic <T extends Comparable> T getMiddle(T[] a){...}

在这个类型变量限制成这个T是实现了这个接口

说明;

多个限定类型用”&”分隔,在限定类型中可以有多个接口,但只能有一个类,并且这个类必须在限定列表的第一个

<Textends ArrayList & comparable & Serializable>




泛型代码与虚拟机;


Java虚拟机是不存在泛型类型对象的,所有的对象都属于普通类。

虚拟机的一种机制:擦除类型参数,并将其替换成特定类型,没有指定特定类型用Object代替,如

classPair<T>变为classPair<Object>

为了保持类型安全性,虚拟机在有必要时插入强制类型转换。

桥方法的合成用来保持多态性。

协变类型允许子类覆盖方法后返回一个更严格的类型。


约束与局限性;

1.不能用基本类型实例化类型参数

Pair<Double>Pair<Integer>,只能用包装类,原因是类型变量为Object

2.运行时类型查询(instanceof)只适用于原始类型

ainstanceof Pair<String>只是查询a是不是Pair类型的

3.不能创建参数化类型数组

Pair<String>[]table = new Pair<String>[10] //error

原因是虚拟机的擦除类型参数机制,解决方法

考虑使用ArrayList<Pair<String>>进行

4.不能实例化类型变量

newT() //error

5.泛型类的静态上下文中的类型变量无效

不能在静态域或者方法中引用类型变量

6.不能抛出或捕获泛型类的实例

Java中,publicclassPair<T>extendsException{...}这种泛型类扩展子Throwable是不合法的,不能通过编译器。也不能再catch子句中使用类型参数

7.注意擦除后的冲突

publicclass Pair<T>{

publicboolea equals(T value){...}

}

从这个类的定义中来看,存在两个equals方法,一个是自身定义的publicboolean equals(T value){...},一个是从Object继承的publicboolean equals(Object obj) {...},但类型擦除以后,前者方法成为了publicboolean equals(Object value){...},而在一个类中同时存在两个方法名和参数一样的方法是不可能的,所以这里引发的冲突是没法通过编译器的。可以通过重新命名方法进行修正。


通配符类型

Pair<?extends Employee>

表示任何泛型Pair类型,它的类型参数是Employee的子类

"?"表示通配符,它的存在必须存在泛型类的类型参数中

通配符的超类型限定

classPair<? super Manager>

直观的讲,带有超类型限定的通配符(super)可以向泛型对象写入(set),带有子类型限定的通配符(extends)可以从泛型对象读取(get)


无限定通配符

无限定通配符去除了超类型和子类型的规则,仅仅用一个"?"表示,并且也只能用于指定泛型类的类型参数中


反射与泛型

Class类是泛型的,String.class实际上是一个Class<String>类的对象







### Java 全面知识点与基础教程 #### 什么是Java(Generics)是一种提供编译时类检查的机制,允许在类、接口和法中定义、传递和操作各种类的对象,而无需明确指定具体类。这种设计可以增强代码的可读性、可维护性,并减少类转换错误[^1]。 --- #### 的关键概念 ##### 1. **泛型类** 泛型类是指可以在类声明时使用一个或多个类参数的类。这些类参数可以用作字段、法返回值或法参数的占位符。 - **基本格式**: ```java public class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; } } ``` - **注意事项**: - 泛型类的实际类是在创建对象时指定的。 - 编译器会在运行时执行类擦除,因此无法在泛型类中直接使用 `new T()` 创建实例[^3]。 --- ##### 2. **接口** 类似于泛型类接口也可以接受类参数。 - **基本格式**: ```java public interface Generator<T> { T next(); } ``` - **注意事项**: - 实现接口的类可以选择固定某个类或者保留特性。 ```java // 固定类 public class StringGenerator implements Generator<String> { @Override public String next() { return "Hello"; } } // 保持 public class GenericGenerator<T> implements Generator<T> { @Override public T next() { return null; // 或者其他逻辑 } } ``` --- ##### 3. **法** 法是指在其签名中包含自己的类参数的法。即使该法属于非泛型类,它仍然可以独立于类的类参数工作。 - **基本格式**: ```java public <T> void method(T param) {} ``` - **示例**: ```java public class Util { public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); } } class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } ``` --- ##### 4. **通配符** 通配符用于表示未知类,增强了使用的灵活性。 - **协变 (`<? extends T>`)**:只允许访问子类的数据。 ```java List<? extends Number> list = new ArrayList<>(); Number num = list.get(0); // 可以获取数据 // list.add(new Integer(1)); 不支持添加任何元素 ``` - **逆变 (`<? super T>`)**:只允许访问父类的数据。 ```java List<? super Integer> list = new ArrayList<>(); list.add(new Integer(1)); // 支持添加Integer及其子类 Object obj = list.get(0); // 获取到的是Object类 ``` - **无界通配符 (`<?>`)**:表示任意类。 ```java List<?> list = new ArrayList<>(); Object obj = list.get(0); ``` --- ##### 5. **类擦除** 类擦除是 Java 中的一个重要概念,指的是在编译阶段会移除所有的信息,在字节码层面不再存在具体的类约束。 - **影响**: - 运行时无法判断的具体类- 静态上下文中不允许使用变量- **解决式**: 使用反射或其他工具(如 TypeToken),可以通过间接手段恢复部分类信息。 --- ##### 6. **数组与** 由于数组具有运行时类检查的能力,而依赖于编译期类检查,这使得两者之间存在冲突。因此,不能创建带有泛型类的数组。 - 错误示例: ```java List<String>[] stringLists = new List<String>[1]; // 编译报错 ``` - 替代案: ```java List<String>[] stringLists = (List<String>[]) new List<?>[1]; ``` --- #### 总结 Java 的核心在于提升代码的安全性和复用能力。通过了解其核心概念(如泛型类接口、法、通配符和类擦除),开发者能够更高效地构建灵活且健壮的应用程序。 --- ### 示例代码 以下是综合运用的知识点的简单例子: ```java public class Main { public static void main(String[] args) { Box<Integer> integerBox = new Box<>(); integerBox.setContent(10); System.out.println(integerBox.getContent()); Pair<String, Integer> pair = new Pair<>("Key", 123); System.out.println(pair.getKey() + ": " + pair.getValue()); List<Number> numbers = Arrays.asList(1, 2.5f, 3L); printNumbers(numbers); List<Object> objects = Arrays.asList("String", new Object(), 42); addObject(objects, "New Element"); } public static <T> void printNumbers(List<? extends Number> list) { for (Number number : list) { System.out.print(number.doubleValue() + " "); } System.out.println(); } public static <T> void addObject(List<? super T> list, T element) { list.add(element); } } class Box<T> { private T content; public void setContent(T content) { this.content = content; } public T getContent() { return content; } } class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值