`
haofenglemon
  • 浏览: 245818 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java generic

    博客分类:
  • user
阅读更多
泛型允许对类型进行抽象,最常见的泛型类是容器类。例如:

List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for(String s: list) {
System.out.println(s);
}    以上例子中,如果试图向list中添加一个Integer对象,那么会导致编译错误。编译器会进行类型检查,这避免了使用非泛型容器类时常见的强制类型转换。泛型类是具有一个或者多个类型变量(type variable)的类。以下是个简单的例子:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Bag<E> {
private List<E> bag = new ArrayList<E>();

public Iterator<E> iterator() {
return this.bag.iterator();
}

public boolean add(E e) {
return this.bag.add(e);
}

public boolean remove(Object o) {
return this.bag.remove(o);
}

public boolean contains(Object e) {
return this.bag.contains(e);
}

public boolean addAll(Bag<? extends E> b) {
return this.bag.addAll(b.bag);
}

public boolean removeAll(Bag<?> b) {
return this.bag.removeAll(b.bag);
}

public boolean containsAll(Bag<?> b) {
return this.bag.containsAll(b.bag);
}
   
public void clear() {
this.bag.clear();
}
  
   List<E> getBag() {
return this.bag;
}
}import java.util.Collections;
import java.util.Comparator;

public class Bags {
public static <E> void sort(Bag<E> b, Comparator<? super E> c) {
Collections.sort(b.getBag(), c);
}

public static <T extends Comparable<? super T>> T max(Bag<T> b) {
return Collections.max(b.getBag());
}

public static <E> void copy(Bag<E> dest, Bag<? extends E> src) {
dest.addAll(src);
}
}    需要注意的是,不能抛出或者捕获泛型类的实例,泛型类继承自Throwable也是不合法的。此外也不能声明泛型类实例的数组,例如以下代码会造成编译错误:

Bag<Integer> b3[] = new Bag<Integer>[10]; // Cannot create a generic array of Bag<Integer>

2 泛型方法
    泛型方法是指带有类型参数的方法,类型变量的位置是修饰符和返回类型之间。在调用泛型方法的时候,具体类型的位置是方法名前的尖括号中,在绝大多数的情况下,由于编译器可以判断出具体类型,因此也可以省略具体类型。以下是个简单的例子:

public class Base {
    public String getName() {
    return "Base";
    }

    public static <T> void print(T t) {
        System.out.println(t.toString());
    }

    public static void main(String args[]) {
    Base base = new Base();
    Base.<Base>print(base);
    Base.print(base);
    }
}    以上例子中的print方法内只能调用T从Object类继承的方法。如果希望将T限定为Base及其子类,那么可以使用extends设定上限,或者使用super设定下限(C++中不能限定参数变量的类型),例如:

public static <T extends Base> void print(T t) {
System.out.println(t.getName());
}    类型变量也可以有多个限定,限定类型用&分割。如果限定类型可以包含多个接口,但是至多有一个类(单继承),如果有一个类,那么它必须是第一个限定类型。例如:

T extends Comparable & Serializable

3 擦除
    编译器会为每个泛型类型自动提供一个原始类型(raw type)。其类型变量会被擦除,并用其限定类型代替,如果没有限定类型,那么使用Object。不能使用原始数据类型作为类型变量,例如没有Bag<int>,但是可以有Bag<Integer>。
    由于类型变量不存在于运行时,因此Java泛型没有C++模板类的代码膨胀问题。但是也有批评人士认为:如果你打算表示“任何类型”,那么Java会把这个任何类型转化为Object。这只能说Java的泛型只是对Object类型的一个泛化而已。Java泛型只是解决了容器类之间的自动转型罢了。

    由于类型变量会被擦除,因此不管泛型类的类型变量是什么,它的所有实例的运行时类是相同的,例如以下代码的输出如下:

Bag<Integer> b1 = new Bag<Integer>();
Bag<String> b2 = new Bag<String>();
System.out.println(b1.getClass());
System.out.println(b2.getClass());
System.out.println(b1.getClass() == b2.getClass());    class Bag
    class Bag
    true

    由于类的静态成员变量和成员方法也被类的所有实例共享,因此不能在类的静态成员变量和成员方法中引用类型变量。例如以下的代码会导致编译错误:

public class Bag<E> {
private static E INSTANCE; // Cannot make a static reference to the non-static type E

public static E getInstance() { // Cannot make a static reference to the non-static type E
return INSTANCE;
}
}    不能通过类型变量构造实例,但是可以通过Class.newInstance和Array.newInstance来构造实例。例如以下的代码会造成编译错误:

public Bag() {
E e1 = new E(); // Cannot instantiate the type E
E e2[] = new E[10];
}    在擦除后,也不能引起冲突。考虑如下代码:

public class Bag<E> {
public boolean equals(E e) { // Name clash: The method equals(E) of type Bag<E> has the same erasure as equals(Object) of type Object but does not override it
return true;
}
}    以上的Bag类的类型变量被擦除后的代码如下:

public class Bag<E> {
public boolean equals(Object e) {
return true;
}
}    其equals方法与Object.equals(Object obj)方法冲突。

4 泛型和继承
    首先考虑如下代码:

public class Base {
    private String id;

    public String getName() {
        return "Base";
    }

    public final String getId() {
        return id;
    }

    public final void setId(String id) {
        this.id = id;
    }
}public class Derived extends Base {

    public String getName() {
        return "Derived";
    }
   
    public static void main(String args[]) {
        //
        Base base = new Base();
        Derived derived = new Derived();
        List<Base> list = new ArrayList<Base>();
        list.add(base);
        list.add(derived);

        //
        List<Derived> list2 = new ArrayList<Derived>();
        List<Base> list3 = list2; // Type mismatch: cannot convert from List<Derived> to List<Base>

        //     
        List list4 = list2;
        list4.add(new Integer(1));
        for(Object o : list4) {
            System.out.println(o);
        }
        try {
            for(Base b : list2) {
                System.out.println(b); // Runtime Exception
            }
        } catch(ClassCastException e) {
            e.printStackTrace();
        }

        //
        List<?> list5 = list2;
    }
}    泛型类可以继承或者实现其它泛型类或接口,例如ArrayList<T>实现了List<T>接口。因此ArrayList<Base>可以被转换成List<Base>,此外向List<Base>中添加base和derived也是合法的。
    但是将list2赋值给list3会导致编译错误,就像关于继承的那个经典问题一样:苹果可以是水果的子类,但是能够装苹果的袋子不是能够装水果的袋子的子类。将list2赋值给list4和是合法的,但是如果向list4中添加一个Integer对象,那么在访问list2的时候会导致运行时异常。
    list5使用了通配符,这在稍后会介绍。



5 通配符
    在引入泛型之前,遍历Collection的方法通常如下:

public static void visit(Collection c) {

    for(Iterator iter = c.iterator(); iter.hasNext(); ) {
        System.out.println(iter.next());
    }
}

public static void main(String args[]) {
    List list = new ArrayList();
    list.add("1");
    visit(list);
}    使用泛型后,可以写成:

public static void visit(Collection<Object> c) {

    for(Object o : c) {
        System.out.println(o);
    }
}

public static void main(String args[]) {
    List<String> list = new ArrayList();
    list.add("1");
    visit(list); // The method visit(Collection<Object>) in the type Test is not applicable for the argument (List<String>)
}    由于List<String>并不是Collection<Object>的子类(Collection<Object>并不是所有类型的集合的父类型),因此调用visit(list)会造成编译错误。为了解决这个问题,Java泛型引入了通配符“?”。Collection<?>是所有类型的集合的父类型,叫做“未知集合”。

    需要注意的是,向“未知集合”中添加对象不是类型安全的,这会导致编译错误,唯一例外的是null。此外从“未知集合”中获得的对象的类型也只能是Object类型。例如:

Collection<?> c = new ArrayList<String>();
c.add("1"); // The method add(capture#1-of ?) in the type Collectoin<capture#1 of ?> is not applicable for the arguments (String)
c.add(null);
Object o = c.get(0);  

5.1 有界通配符
5.1.1 上界
    可以使用extends为通配符限定上界,例如以下代码中限定了通配符的上界是Base类:

public static void visit(Collection<? extends Base> c) {

    for(Base b : c) {
        System.out.println(b);
    }
}

public static void main(String args[]) {
    List<Base> list = new ArrayList<Base>();
    list.add(new Base());
    list.add(new Derived());
    visit(list);
}     在visit方法中,同样不能向c中添加非null对象。例如以下代码会造成编译错误:

public static void visit(Collection<? extends Base> c) {
    c.add(new Derived()); // The method add(capture#1-of ? extends Base) in the type Collection<capture#1 -of ? extends Base> is not applicable for the arguments(Derived)
}    有时也可以不使用通配符而达到相同的目的,例如:

public static <T> void copy(List<T> dest, list< ? extends T> src) {
...
}

public static <T, S extends T> void copy(List<T> dest, List<S> src) {
...


5.1.2 下界
    可以使用super为通配符限定下界,考虑以下代码:

public class Derived extends Base implements Comparable<Object> {
    public int compareTo(Object obj) {
        return System.identityHashCode(this) - System.identityHashCode(obj);
    }

    public static <T extends Comparable<T>> T max(Collection<T> c) {
        return Collections.max(c);
    }

    public static void main(String args[]) {
        List<Derived> list = new ArrayList<Derived>();
        list.add(new Derived());
        Derived.max(list); // Bound mismatch: The generic method max(Collection<T>) of type Derived is not applicable for the arguments (List<Derived>). The inferred type Derived is not a valid substitute for the bounded parameter <T extends Comparable<T>>
    }
}    以上的max方法中限定了T类型必须实现了Comparable<T>接口,那么在main方法中以list为参数调用max方法会导致编译错误,这是因为Derived类没有实现Comparable<Derived>接口。这样的限制有些严格,如果将max方法改成如下方式,可以带来更大的灵活性。

public static <T extends Comparable<? super T>> T max(Collection<T> c) {
    return Collections.max(c);
}    以下的代码虽然没有太大的意义,但是需要注意的是,在print方法中,p.setFirst()方法和p.setSecond()方法的参数类型为T,但是p.getFirst()方法和p.getSecond()方法的返回值类型只能是Object型。

public class Pair<T> {
    //
    private T first;
    private T second;

    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }

    public void setFirst(T first) {
        this.first = first;
    }

    public T getSecond() {
        return second;
    }

    public void setSecond(T second) {
        this.second = second;
    }

    public void print(Pair<? super T> p) {
        Object first = p.getFirst();
        Object second = p.getSecond();
        System.out.println("first: " + first + ",  second: " + second);
    }

    public static void main(String args[]) {
        Pair<Derived> p1 = new Pair<Derived>(new Derived(), new Derived());
        Pair<Base> p2 = new Pair<Base>(new Base(), new Base());
        p1.print(p2);
    }
}  

5.2 通配符捕获
    考虑为Pair类编写一个方法,用于交换first和second。由于通配符不是变量类型,那么如下的写法是非法的:

? t = p.getFirst();    此外,以下的写法也会导致编译错误:

public static void swap(Pair<?> p) {
    Object first = p.getFirst();
    p.setFirst(p.getSecond()); // //The method setFirst(captur#4-of ?) in the type Pair<capture#4-of ?> is not applicable for the arguments (capture#5-of ?)
    p.setSecond(first); // The method setFirst(captur#6-of ?) in the type Pair<capture#6-of ?> is not applicable for the arguments (Object)
}    可以通过一个额外的泛型方法中的类型参数来捕获通配符,例如:

public static void swap(Pair<?> p) {
    doSwap(p);
}

private static <T> void doSwap(Pair<T> p) {
    T first = p.getFirst();
    p.setFirst(p.getSecond());
    p.setSecond(first);
}

6 泛型和反射
    Class类是泛型的,而且实现了java.lang.reflect.Type接口。Class类没有公共构造方法。Class对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。Class类的newInstance方法通过默认的构造函数返回类型为T的对象。例如:

public static <T> Pair<T> make(Class<T> c) throws InstantiationException, IllegalAccessException {
    return new Pair<T>(c.newInstance(), c.newInstance());
}

Pair<String> ps = Pair.make(String.class);    接口 TypeVariable<D extends GenericDeclaration>,ParameterizedType, WildcardType,GenericArrayType继承自Type,用于通过反射获得泛型类型的信息(尽管进行了擦除)。
    TypeVariable 表示各种类型变量。其getName()方法返回在源码中声明的变量名;getBounds()方法返回类型变量上边界Type对象数组;getGenericDeclaration()返回GenericDeclaration对象,以表示声明此类型变量的一般声明。
    ParameterizedType 表示参数化类型,例如Collection<String>。其getRawType()返回原始类型,也就是声明此类型的类或接口;getActualTypeArguments()方法返回表示此类型实际类型参数的 Type对象数组。
    WildcardType 表示一个通配符类型表达式,如 ?、? extends Number 或 ? super Integer。getLowerBounds()方法返回类型变量下边界Type对象数组;getUpperBounds()方法返回类型变量上边界Type对象数组。
    GenericArrayType 表示一种数组类型,其组件类型为参数化类型或类型变量。getGenericComponentType() 返回此数组的元素类型的Type对象。
    关于以上接口的详细文档,请参考Javadoc。以下是一个简单的例子,用于获得类型变量的Class类型:

import java.lang.reflect.ParameterizedType;

public abstract class GenericBase<T extends Comparable<? super T>> {
    //
    private Class<T> clazz;

    @SuppressWarnings("unchecked")
    public GenericBase() {
        ParameterizedType pt = (ParameterizedType)getClass().getGenericSuperclass();
        clazz = (Class<T>)pt.getActualTypeArguments()[0];
    }

    public Class<T> getClazz() {
        return clazz;
    }

    public void setClazz(Class<T> clazz) {
        this.clazz = clazz;
    }
}

public class GenericDerived extends GenericBase<String> {
    public static void main(String args[]) {
        GenericDerived gd = new GenericDerived();
        System.out.println(gd.getClazz());
    }
}
分享到:
评论

相关推荐

    Java Generic 介绍 (pdf)

    标题中的“Java Generic 介绍”涵盖了泛型的基本概念、使用方式以及其背后的原理。以下是对这一主题的详细阐述: 1. **泛型的基本概念**:泛型是Java中的一种模板类或接口,它允许开发者在定义类、接口和方法时使用...

    Java - Generic

    在Java编程语言中,泛型(Generic)是一个强大的特性,它允许我们在编译时检查类型安全,并且可以消除运行时的类型转换。泛型引入的主要目的是提高代码的重用性、安全性和效率,同时减少类型转换的繁琐工作。这篇...

    android 浅复制和深复制-Java Generic Deep Copy 篇

    本文将深入探讨Java中的浅复制和深复制,并以Android为背景,结合具体的实例讲解如何实现Java泛型深拷贝。 首先,我们要理解浅复制和深复制的概念。浅复制(Shallow Copy)是指创建一个新的对象,然后将原对象引用...

    java-generic.rar_泛型

    在《java-generic.rar》压缩包中包含的《java generic.pdf》文件,很可能是详细阐述了以下几个关于Java泛型的核心知识点: 1. **类型参数化**:泛型的核心在于类型参数化,这使得我们可以在定义类、接口和方法时...

    Japag - Java generic command line GUI-开源

    【Japag - Java generic command line GUI 开源项目详解】 Japag,全称为 Java Application for Providing a Generic GUI,是一个开源项目,专为那些希望通过图形用户界面(GUI)而非命令行来运行各种命令行应用...

    Java高手真经(编程基础卷)光盘全部源码

    看到很多人都分卷打包的,下载很是不方便,还浪费积分,我就整合压缩打包到一个包里面,里面包含全部...javageneric.zip 27.Java泛型编程 javaannotation.zip 28.Java注释符编程 javafeature.zip 29.Java5.0语言新特性

    java高手真经 光盘源码

    java高手真经 全光盘源代码 打包rar 第1部分(2个程序包) HelloWorld.zip 01.Java入门样例Hello...javageneric.zip 27.Java泛型编程 javaannotation.zip 28.Java注释符编程 javafeature.zip 29.Java5.0语言新特性

    Java高手真经(编程基础卷)光盘全部源码 免积分

    看到那些要积分的很不酸,发布免费版本。 第1部分(2个程序包) HelloWorld.zip 01.Java入门样例...javageneric.zip 27.Java泛型编程 javaannotation.zip 28.Java注释符编程 javafeature.zip 29.Java5.0语言新特性

    Java泛型编程指南.pdf

    ### Java泛型编程指南知识点详解 #### 一、绪论:理解Java泛型的重要性与背景 **1.1 泛型的基本概念** 泛型是一种在编程语言中支持编写类型安全的通用函数或类的能力。在Java中引入泛型的主要目的是为了提供更...

    java 泛型接口示例

    Java 泛型是Java SE 5.0引入的一项重要特性,极大地增强了代码的类型安全性和重用性。泛型接口是泛型在接口中的应用,它允许我们在接口中定义带有类型参数的方法,使得实现该接口的类可以使用不同的数据类型。下面...

    Generic_MT4_java_generic_mt4_

    标题中的"Generic_MT4_java_generic_mt4_"暗示了一个与MetaTrader 4 (MT4)交易平台相关的编程项目,可能是一个自定义的智能交易系统(Expert Advisor,EA)或指标,使用了Java语言,并且具有通用性。这个“Generic_...

    Java中的泛型方法演示代码

    Java中的泛型是Java SE 5.0引入的一项重要特性,极大地增强了代码的类型安全性和重用性。泛型方法是泛型概念的一个关键部分,它允许我们在方法签名中使用类型参数,从而使方法能够处理不同类型的参数。在这个“Java...

    Java1.5泛型指南中文版(Java1.5 Generic Tutorial)

    Java1.5泛型指南中文版(Java1.5 GenericTutorial)

    java 开发技术大全10

    ### Java开发技术大全10:深入理解泛型 #### 10.1 泛型的本质 泛型在Java中是一项重要的特性,它被引入到JDK 1.5中,目的是为了提高代码的复用性和类型安全性。泛型的本质是类型参数化,即允许开发者在声明类或...

    java collection framework

    此外,还介绍了一种名为 JGL (Java Generic Library) 的第三方库,该库在 Java Collection Framework 出现之前就已经存在,并且提供了一些额外的功能。 #### 八、总结 《Java Collection Framework》这本书不仅是...

    java 泛型的使用 详细讲解

    ### Java泛型的使用详细讲解 #### 一、引言 在Java开发中,泛型是一种重要的语言特性,它能够帮助开发者在不增加代码量的情况下处理多种数据类型,同时还能保持代码的清晰度和可读性。本文将详细介绍Java泛型的...

Global site tag (gtag.js) - Google Analytics