Java中的泛型/范型
维基百科中关于Java泛型的描述
Java 泛型的参数只可以代表类,不能代表个别对象。由于Java泛型的类型参数之实际类型在编译时会被消除,所以无法在运行时得知其类型参数的类型,而且无法直接使用基本值类型作为泛型类型参数。Java编译程序在编译泛型时会自动加入类型转换的编码,故运行速度不会因为使用泛型而加快。
由于运行时会消除泛型的对象实例类型信息等缺陷经常被人诟病,Java及JVM的开发方面也尝试解决这个问题,例如Java通过在生成字节码时添加类型推导辅助信息,从而可以通过反射接口获得部分泛型信息。通过改进泛型在JVM的实现,使其支持基本值类型泛型和直接获得泛型信息等。
Java允许对个别泛型的类型参数进行约束,包括以下两种形式(假设
T
是泛型的类型参数,C
是一般类、泛类,或是泛型的类型参数):
T
实现接口I
。T
是C
,或继承自C
。
泛型Since jdk1.5
摘自《Java代码与架构完美优化》
泛型的本质是参数化类型,所操作的数据类型被指定为一个参数。
Java中的泛型在编译器中实现,而不是在虚拟机中实现的,虚拟机对于泛型是一无所知的。因此,编译器一定要把泛型类修改为普通类,才能够在虚拟机中运行。Java中把这种技术称为擦除,泛型代码经过擦除后变成原生类型。
源代码>>>泛型>>>编译器>>>字节码(*.class文件)>>>JVM(类装载器,字节码校验器,解释器)>>>操作系统平台
使用泛型的优势
类型安全以及不需要进行类型转换
类型安全性:我们只能在泛型中只保存一种类型的对象。 它不允许存储其他对象。因此,也不再需要进行类型转换。下面给出使用泛型和不使用泛型的区别。
List list = new ArrayList();
list.add("Hello Generics");
String s = (String) list.get(0); //需要类型转换
List<String> list = new ArrayList<String>();
list.add("Hello Generics");
String s = list.get(0); //不需要类型转换
类型检查从运行时挪到编译时
编译时检查:在编译时检查,所以运行时不会出现问题。 良好的编程策略表明,在编译时处理这个问题要比运行时好得多。
使用泛型的注意事项
- 在static方法中不可以使用泛型,泛型变量也不可以使用static关键字来修饰
- 泛型常用符号的含义-T(Type)、K(Key)、V(Value)、E(Element),N(Number)尽管其它形式也可以作为变量的符号,但是我们习惯使用这几种符号。
- 泛型只适用于对象,基本类型不适用,但是可以使用基本类型的包装类来实现,比如:
List<Integer> intList = new ArrayList<Integer>();
intList.add(1); //自动将1转换成Integer
intList.add(2);
-
开发人员在使用泛型的时候,很容易根据自己的直觉而犯一些错误。比如一个方法如果接收List<Object>作为形式参数,那么如果尝试将一个List<String>的对象作为实际参数传进去,却发现无法通过编译。虽然从直觉上来说,Object是String的父类,这种类型转换应该是合理的。但是实际上这会产生隐含的类型转换问题,因此编译器直接就禁止这样的行为。http://www.infoq.com/cn/articles/cf-java-generics
泛型使用举例
创建参数化类型-泛型类
//simple
class MyGen<T> {
T obj;
void add(T obj) {
this.obj = obj;
}
T get() {
return obj;
}
}
public class Generics {
public static void main(String args[]) {
MyGen<Integer> m = new MyGen<Integer>();
m.add(2);
//m.add("test"); //Compile time error
System.out.println(m.get());
}
}
//complex from agile java
import java.util.*;
public class MultiHashMap <K,V> {
private Map<K, List<V>> map = new HashMap<K, List<V>>();
public static <K extends Comparable<K>,V> List<K>
sortedKeys(MultiHashMap<K, V> map) {
List<K> keys = new ArrayList<K>();
keys.addAll(map.keys());
Collections.sort(keys);
return keys;
}
public Set<K> keys() {
return map.keySet();
}
public int size() {
return map.size();
}
public void put(K key, V value) {
List<V> values = map.get(key);
if (values == null) {
values = new ArrayList<V>();
map.put(key, values);
}
values.add(value);
}
public List<V> get(K key) {
return map.get(key);
}
protected Set<Map.Entry<K, List<V>>> entrySet() {
return map.entrySet();
}
public interface Filter<T> {
boolean apply(T item);
}
public static <K,V> void filter(final MultiHashMap<K, ? super V> target,
final MultiHashMap<K, V> source,
final Filter<? super V> filter) {
for (K key : source.keys()) {
final List<V> values = source.get(key);
for (V value : values)
if (filter.apply(value))
target.put(key, value);
}
}
}
泛型方法
public class Generics {
public static < E > void printArray(E[] elements) {
for ( E element : elements){
System.out.println(element );
}
System.out.println();
}
public static void main( String args[] ) {
Integer[] intArray = {6, 66, 666};
String[] stringArray = { "6","66","666" };
System.out.println( "Printing Integer Array" );
printArray( intArray );
System.out.println( "Printing String Array" );
printArray( stringArray );
}
}
上限
每个类型参数都有一个缺省为Object的上限,你可以将类型参数限制为不同的上限。这里需要使用extends关键字来指定某个类型参数的上限。
//from agile java
import java.util.*;
public class EventMap<K extends Date,V>
extends MultiHashMap<K,V> {
public List<V> getPastEvents() {
List<V> events = new ArrayList<V>();
for (Map.Entry<K,List<V>> entry: entrySet()) {
K date = entry.getKey();
if (hasPassed(date))
events.addAll(entry.getValue());
}
return events;
}
private boolean hasPassed(K date) {
Calendar when = new GregorianCalendar();
when.setTime(date);
Calendar today = new GregorianCalendar();
if (when.get(Calendar.YEAR) != today.get(Calendar.YEAR))
return when.get(Calendar.YEAR) < today.get(Calendar.YEAR);
return when.get(Calendar.DAY_OF_YEAR) <
today.get(Calendar.DAY_OF_YEAR);
}
}
通配符wildcard
java允许使用一个通配符?来表示任意可能的类型,此外你可以使用extends子句限制通配符的上限。
import java.util.ArrayList;
import java.util.List;
abstract class Shape {
abstract void draw();
}
class Rectangle extends Shape {
void draw() {
System.out.println("drawing rectangle");
}
}
class Circle extends Shape {
void draw() {
System.out.println("drawing circle");
}
}
public class Generics {
// creating a method that accepts only child class of Shape
public static void drawShapes(List<? extends Shape> lists) {
for (Shape s : lists) {
s.draw(); // calling method of Shape class by child class instance
}
}
public static void main(String args[]) {
List<Rectangle> list1 = new ArrayList<Rectangle>();
list1.add(new Rectangle());
List<Circle> list2 = new ArrayList<Circle>();
list2.add(new Circle());
list2.add(new Circle());
drawShapes(list1);
drawShapes(list2);
}
}
demo来源
- https://www.javatpoint.com/generics-in-java
- agile java