Java5新特性-泛型的使用总结(一)

Java5的泛型的语法,已经有很多帖子讲了,这里依据我的一些个人理解做一次总结,一方面是为了将我近一段时间对泛型的学习落实到纸面,毕竟有很多想 法,如果只是存在于脑子里,过一段时间也就淡忘了,总要留下点文字便于以后回顾;另一方面,也希望拿出点东西来与大家交流分享,这样才能不断的得到提高。

泛型是什么
简单的来说,泛型可以认为是类型参数化的技术。相对于类型的参数化,在传统的java代码中,我们可以理解为所有的参数都是值的参数化,例如以下代码:
 1 package org.dbstar.generic;
 2 
 3 public class ValueHolder {
 4     private String value;
 5 
 6     public ValueHolder(String value) {
 7         this.value = value;
 8     }
 9 
10     public String getValue() {
11         return value;
12     }
13 
14     public void setValue(String value) {
15         this.value = value;
16     }
17 }
其中的成员变量、构造函数参数和方法参数,都可以认为是值的参数化的体现。但是,不管值如何变化,值的类型只能局限于String及其子类,如果我们想让 ValueHolder同时也能持有一个Integer类型的值,那么在传统的Java代码中,只能将成员变量的类型定义为Object,如下:
 1 package org.dbstar.generic;
 2 
 3 public class ValueHolder {
 4     private Object value;
 5 
 6     public ValueHolder(Object value) {
 7         this.value = value;
 8     }
 9 
10     public Object getValue() {
11         return value;
12     }
13 
14     public void setValue(Object value) {
15         this.value = value;
16     }
17 }
使用Object版的ValueHolder的代码看起来会像这样:
1     public static void main(String[] args) {
2         ValueHolder vhi = new ValueHolder(new Integer(1));
3         ValueHolder vhs = new ValueHolder("abc");
4         ValueHolder vho = new ValueHolder(new Object());
5         System.out.println("ValueHolder of Integer=" + vhi.getValue());
6         System.out.println("ValueHolder of String=" + vhs.getValue());
7         System.out.println("ValueHolder of Object=" + vho.getValue());
8     }
输出结果为:
1 ValueHolder of Integer=1
2 ValueHolder of String=abc
3 ValueHolder of Object=java.lang.Object@757aef
幸运的是,Java5给我们提供了一个将value的类型也作为一个参数来设置的方法,那就是泛型,下面是将value的类型也参数化后的 ValueHolder实现:
 1 package org.dbstar.generic;
 2 
 3 public class ValueHolder<E> {
 4     private E value;
 5 
 6     public ValueHolder(E value) {
 7         this.value = value;
 8     }
 9 
10     public E getValue() {
11         return value;
12     }
13 
14     public void setValue(E value) {
15         this.value = value;
16     }
17 }
其中,value就是值的参数化,value的类型现在定义为E,<E>就是类型的参数化。使用ValueHolder的代码也相应发生了变 化:
1         ValueHolder<Integer> vhi = new ValueHolder<Integer>(new Integer(1));
2         ValueHolder<String> vhs = new ValueHolder<String>("abc");
3         ValueHolder<Object> vho = new ValueHolder<Object>(new Object());
4         System.out.println("ValueHolder of Integer=" + vhi.getValue().intValue());
5         System.out.println("ValueHolder of String=" + vhs.getValue().substring(1));
6         System.out.println("ValueHolder of Object=" + vho.getValue());
根据ValueHolder类型定义的不同,类型参数<E>在不同场合代表了不同的实际类型。

泛型的赋值
与值参数的赋值不同的是,类型参数的赋值有其特殊性,下面来逐一说明:
A。同类型赋值:
1         ValueHolder<Integer> vhi = new ValueHolder<Integer>(new Integer(1));
2         ValueHolder<Integer> vhi2 = vhi;
3         vhi2.setValue(new Integer(2));
4         System.out.println("ValueHolder of Integer2=" + vhi2.getValue().intValue());
这种赋值后的变量,与源变量具有完全一致的操作,包括获取泛型变量和在方法参数中设置泛型变量。
B。超类型赋值:
1         ValueHolder<Integer> vhi = new ValueHolder<Integer>(new Integer(1));
2         ValueHolder<Number> vhi3 = vhi;//实际上,这是不允许的,会导致编译错误
3         ValueHolder<? extends Number> vhi2 = vhi;//必须要这样写
4         System.out.println("ValueHolder of Integer2=" + vhi2.getValue().intValue());
5         vhi2.setValue(new Integer(2));//超类型赋值后,操作受到限制,会导致编译错误
6         vhi2.setValue(null);//只有设置null值才是合法的,其他值一概不允许设置
这种赋值后的变量,能调用返回值为泛型变量的方法,但是只能使用新泛型变量类型的方法,而不能再使用原有泛型变量类型上的方法,这样说有点绕,举例来说, 上面的vhi2就只能使用Number类型上定义的方法,而无法再使用原先在Integer类型上定义的方法了。
可赋值的超类型可以是原类型的任何超类型或者接口,这些赋值都是类型安全的,不会引发任何编译时警告。例如:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->1         ValueHolder<? extends Number> vhn = vhi;//超类
2         ValueHolder<? extends Object> vho2 = vhi;//超类
3         ValueHolder<? extends Comparable> vhc = vhi;//接口
赋值后的带?的泛型,还能继续赋值给超类,例如:
<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->1         ValueHolder<? extends Number> vhn = vhi;
2         ValueHolder<? extends Object> vho3 = vhn;
ValueHolder<?>等效于ValueHolder<? extends Object>,因为Object是所有类的超类。
C。子类型赋值:
1         ValueHolder<? extends Number> vhi2 = vhi;//必须要这样写
2
         @SuppressWarnings("unchecked")
3         ValueHolder<Integer> vhi3 = (ValueHolder<Integer>) vhi2;
和值参数的赋值一样,超类型的泛型变量往子类型的泛型变量赋值,需要强制转换,转换后的vhi3与vhi具有完全一致的操作。但是,需要注意的一点是,这 种转换会导致一个unchecked的编译时警告,而且,若无法完成转换,会导致一个运行时的ClassCastException异常,例如:
1         @SuppressWarnings("unchecked")
2         ValueHolder<Double> vhi3 = (ValueHolder<Double>) vhi2;//这里不会抛出异常
3         vhi3.setValue(new Double(12.5));
4         System.out.println("ValueHolder of Double=" + vhi3.getValue().doubleValue());
5         System.out.println("ValueHolder of Integer=" + vhi.getValue().intValue());//会在这里抛出ClassCastException
上例中,vhi2将Integer类型转换成了超类型Number,而vhi3又将Number强类型转换成了Double,这也是允许的,不会引发编译 时错误或者运行时错误。下面甚至还可以给vhi3设置一个Double的值,值得注意的是,vhi3其实就是vhi,我们给一个Integer的变量设置 了一个Double的值,却没有引发任何异常,还真是诡异的很。随后,我们在调用vhi.getValue().intValue()时,才终于引发了 ClassCastException。由此可见,这种强类型转换有时候会使错误变得非常隐晦难于发现,我们应该尽量避免这种转换的使用。
D。赋值给传统代码(row type赋值):
1         @SuppressWarnings("unchecked")
2         ValueHolder vh = vhi;
3         vh.setValue(new Integer(3));
不指定类型参数的泛型类,称之为row type,这是Java5泛型为了与老版本的代码兼容而选择的一种处理方式。Row type的泛型类中,所有的类型参数都被当做Object来处理,也就是说,ValueHolder可以当做 ValueHolder<Object>来处理,但是与ValueHolder<Object>又有所不同,因为 ValueHolder<Integer>是不能被转换为ValueHolder<Object>的,而能转换的ValueHolder<? extends Object>又不能调用setValue方法(当然可以设置null值,而其他非null值不能设置)。值得注意的是,转换到传统代码,以及使用 传统代码调用有泛型参数的方法,会引起一个unchecked的编译时警告。
E。传统代码(row type)赋值给泛型
与第三点:子类型赋值类似,可以将传统代码看做是ValueHolder<? extends Object>,同样需要强类型转换,以及存在类型安全问题。
### 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、付费专栏及课程。

余额充值