泛型

来源:互联网 发布:淘宝怎么看申请时间 编辑:程序博客网 时间:2024/06/06 10:01
泛型例子:
public class Holder <T> {
private T a;
public Holder(T a){this.a = a;}
public void set(T a){this.a = a;}
public T get() { return a; }
public static void main(String[] args){
Holder <Automobile> h = new Holder<Automobile>(new Automobile());
Automobile a = h.get();
a.helloWorld();
}

}

class Automobile{
public void helloWorld(){System.out.println("HelloWorld!");}
}

元祖,它是将一组对象直接打包存储于其中的一个单一对象,这个容器对象允许读取其中元素,但是不允许向其中存放新的对象
public class ThreeTuple<A,B,C> extends TwoTuple<A,B>

泛型接口:
public interface Generator<T> { T next(); }

Java泛型的一个局限性:基本类型无法作为类型参数。不过,Java SE5具备了自动打包和自动拆包的功能,可以很方便地在基本类型和其相应的包装器类型之间进行转换。

泛型方法:
可以在类中包含参数化方法,而这个方法所在的类可以是泛型类,也可以不是泛型类。也就是说,是否拥有泛型方法,与其所在的类是否是泛型没有关系

泛型方法使得该方法能够独立于类而产生变化。以下是一个基本的指导原则:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更清楚明白。另外,对于一个static的方法而言,无法访问泛型类的类型参数,所在,如果static方法需要使用泛型能力,就必须使其成为泛型方法。

定义泛型方法:
public <T> void f(T x){}

注意:当使用泛型类时,必须在创建对象的时候指定类型参数的值,而使用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为类型参数推断。
    如果调用f()时传入基本类型,自动打包机制就会介入其中,将基本类型的值包装为对应的对象。

类型推断只对赋值操作有效,其他时候并不起作用。如果你将一个泛型方法调用的结果作为参数,传递给另一个方法,这时编译器并不会执行类型推断。在这种情况下,编译器认为:调用泛型方法后,其返回值被赋给一个Object类型的变量。

在泛型方法中,可以显示地指明类型,不过这种语法很少使用。要显示地指明类型,必须在点操作符与方法名之间插入尖括号,然后把类型置于尖括号内。如果是在定义该方法的类的内部,必须在点操作符之前使用this关键字,如果是使用static的方法,必须在点操作符之前加上类名。

匿名内部类:
泛型还可以应用于内部类以及匿名内部类

擦除:
在泛型代码内部,无法获得任何有关泛型参数类型的信息

Java泛型是使用擦除来实现的,这意味着当你在使用泛型时,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此List<String>和List<Integer>在运行时事实上是相同的类型。这两种形式都被擦除成它们的“原生”类型,即List

在基于擦除的实现中,泛型类型被当作第二类类型,即不能在某些重要的上下文环境中使用的类型。泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上界。例如,诸如List<T>这样的类型注解将被擦除为List,而普通的类型变量在未指定边界的情况下将被擦除为Object。

注意,对于在泛型中创建的数组,使用Array.newInstance()是推荐方式。

在泛型中的所有动作都发生在边界处——对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。这有助于澄清对擦除的混淆,记住,“边界就是发生动作的地方”

擦除的补偿:
擦除丢失了在泛型代码中执行某些操作的能力。任何在运行时需要知道确切类型信息的操作都将无法工作:
public class Erased<T>
{
    private final int SIZE=100;
    public static void f(Object arg)
    {
        if(arg instanceof T) { }            //Error
        T var = new T();                      //Error
        T[] array = new T[SIZE] ;       //Error
        T[] array = (T) new Object[SIZE];    //Unchecked warning
    }
}
偶尔可以绕过这些问题编程,但是有时必须通过引入类型标签来对擦除进行补偿。这意味着你需要显示地传递你的类型的Class对象,以便你可以在类型表达式中使用它。

class Building{}
class House extends Building{}

public class ClassTypeCapture<T>
{
    Class<T> kind;
    public ClassTypeCapture(Class<T> kind)
    {
        this.kind = kind;
    }
    public boolean f(Object arg)
    {
        return kind.isInstance(arg);
    }
    public static void main(String[] args)
    {
        ClassTypeCapture<Building> ctt1 = new ClassTypeCapture<Building>(Building.class);
        System.out.println(ctt1.f(new Building()));
        System.out.println(ctt1.f(new House()));
        ClassTypeCapture<House> ctt2 = new ClassTypeCapture<House>(House.class);
        System.out.println(ctt1.f(new Building()));
        System.out.println(ctt1.f(new House()));
    }
}
/*Output:
true
true
false
true
*///
编译器将确保类型标签可以匹配泛型参数

解决new T():
1、传递一个工厂对象,并使用它来创建新的实例。最便利的工厂对象就是Class对象,因此如果使用类型标签,那么你就可以使用newInstance()来创建这个类型的新对象:
interface FactoryI<T>
{
    T create();
}

class Foo2<T>
{
    private T x;
    public < F extends FactoryI<T>> Foo2(F factory)
    {
        x=factory.create();
    }
}

class IntegerFactory implements FactoryI<Integer>
{
    public Integer create()
    {
        return new Integer(0);
    }
}

class Widget
{
    public static class Factory implements FactoryI<Widget>
    {
        public Widget create()
        {
            return new Widget();
        }
    }
}

public class FactoryConstraint
{
    public static void main(String[] args)
    {
        new Foo2<Integer>(new IntegerFactory());
        new Foo2<Widget>(new Widget.Factory());
    }
}

2、另一种方式是模板方法设计模式
abstract class GenericWithCreate<T>
{
    final T element;
    GenericWithCreate() { element = create(); }
    abstract T create();
}

class X {}

class Creator extends GenericWithCreate<X>
{
    X create() { return new X(); }
    void f()
    {
        System.out.println(element.getClass().getSimpleName());
    }
}

public class CreatorGeneric
{
    public static void main(String[] args)
    {
        Creator c = new Creator();
        c.f();
    }
}
/*output:
X
*////

解决new T[SIZE]:
不能创建泛型数组。一般的解决方案是在任何想要创建泛型数组的地方都使用ArrayList:
import java.util.*;

public class ListOfgenerics<T>
{
    private List<T> array = new ArrayList<T>();
    public void add(T item) { array.add(item); }
    public T get(int index) { return array.get(index); }
}
这里你将获得数组的行为,以及由泛型提供的编译期的类型安全。






0 0