java编程思想读书笔记 第十五章 泛型 (泛型方法)

来源:互联网 发布:prim算法描述 编辑:程序博客网 时间:2024/05/18 00:47

泛型方法
首先需要知道的是,可以在类中包含参数化方法,而这个方法所在的类可以是泛型类,也可以不是泛型类。也就是说,是否拥有泛型方法,与其所在的类是否是泛型没有关系。泛型方法使得该方法能够独立于类而产生变化。一个基本原则是:无论何时,只要你能做到,你就应该尽量使用泛型方法。也就是说,如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为它可以使事情更清楚明白。另外,对于一个 static 的方法而言,无法访问泛型类的类型参数,所以,如果 static 方法需要使用泛型能力,就必须使其成为泛型方法。
要定义泛型方法,只需将泛型参数列表置于返回值之前,比如:public void f(T x){};

1)杠杆利用类型参数推断
在泛型方法中,类型参数推断可以简化一部分工作。例如,编写一个工具类,它包含各种各样的static方法,专门用来创建各种常用的容器对象,例如:

public class New {    public static <K,V> Map<K,V> map() {        return new HashMap<K, V>();    }    public static <T> List<T> list() {        return new ArrayList<T>();    }    public static <T> LinkedList<T> lList(){        return new LinkedList<T>();    }    public static <T> Set<T> set(){        return new HashSet<T>();    }    public static <T> Queue<T> queue(){        return new LinkedList<T>();    }    public static void main(String[] args) {        Map<String, List<String>> sls = New.map();        List<String> ls = New.lList();        Set<String> ss = New.set();        Queue<String> qs = New.queue();    }}

main()方法演示了如何使用这个工具类,类型参数推断避免了重复的泛型参数列表。类型推断只对复制操作有效,其他时候并不起作用。如果你将一个泛型方法调用的结果作为参数,传递给另一个方法,这时编译器并不会执行类型推断。
显示的类型说明,在泛型方法中,可以显示地指明类型,不过这种语法很少用。要显示地指明类型,必须在点操作符之前使用this关键字,如果是使用static的方法,必须在点操作符之前加上类名。例如New.<Person,List<Pet>> map();

2)可变参数与泛型方法
泛型方法与可变参数列表能够很好地共存,例如:

public class GenericVarargs {    public static <T> List<T> makeListT(T... args){        List<T> reList = new ArrayList<T>();        for (T item : args) {            reList.add(item);        }        return reList;    }    public static void main(String[] args) {        List<String> ls = makeListT("A");        System.out.println(ls);        ls = makeListT("A","B","C");        System.out.println(ls);        ls = makeListT("ABCDEFHIJKLMNOPQRSTUVWXYZ".split(""));        System.out.println(ls);    }}

makeListT()方法展示了与标准类库中java.util.ArrayList()方法相同的功能。

3)用于Generator的泛型方法
利用生成器,可以很方便地填充一个Collection,而泛型化这种操作时具有实际意义的,例如:

public class Generators {    public static <T> Collection<T> fill(Collection<T> coll,Generator<T> gen,int n) {        for (int i = 0; i < n; i++) {            coll.add(gen.next());        }        return coll;    }    public static void main(String[] args) {        Collection<Coffee> coffees = fill(new ArrayList<Coffee>(), new CoffeeGenerator(), 4);        for (Coffee coffee : coffees) {            System.out.println(coffee);        }           }}

其中Generator接口、Coffee类以及CoffeeGenerator类在上一篇博客中有,在这个例子中,fill()方法使如何透明地应用于Coffee的容器。

4)一个通用的Generator
下面的程序可以为任何类构造一个Generator,只要该类具有默认的构造器。为了减少类型声明,它提供了一个泛型方法,用以生成BasicGenerator:

public class BasicGenerator<T> implements Generator<T>{    private Class<T> type;    public BasicGenerator(Class<T> type){        this.type = type;    }    @Override    public T next() {        try {            return type.newInstance();        } catch (Exception e) {            throw new RuntimeException(e);        }    }    public static <T> Generator<T> create(Class<T> type) {        return new BasicGenerator<T>(type);    }}

这个类提供了一个基本实现,用以生成某个类的对象。这个类必须具备两个特点:(1)它必须声明为public。(因为BasicGenerator与要处理的类在不同的包中,所以该类必须声明为public,并且不只具有包内访问权限。)(2)它必须具备默认的构造器(无参的构造器)。要创建这样的BasicGenerator对象,只需调用create()方法,并传入想要生成的类型。泛型化的create()方法允许执行BasicGenerator.create(MyType.class),而不必执行麻烦的new BasicGenerator(MyType.class)。
例如,下面是一个具有默认构造器的简单的类:

public class CountObject {    private static long counter = 0;    private final long id = counter ++;    public long id(){        return id;    }    public String toString(){        return "CountObject" + id;    }}

CountObject类能够记录下它创建了多少个CountObject实例,并通过toString()方法告诉我们其编号。
使用BasicGenerator,你可以很容易地位CountObject创建一个Generator:

public class BasicGeneratorDemo {    public static void main(String[] args) {        Generator<CountObject> generator = BasicGenerator.create(CountObject.class);        for (int i = 0; i < 5; i++) {            System.out.println(generator.next());        }    }}output:CountObject0CountObject1CountObject2CountObject3CountObject4

可以看到,使用泛型方法创建Generator对象,大大减少了我们要编写的代码。java泛型要求传入Class对象,以便也可以在create()方法中用它进行类型推断。
5)一个Set的实用工具
作为泛型方法的另一个示例,通过使用泛型方法,可以很方便地做到使用Set来表达数学中的关系式,而且可以应用于多种类型:

public class Sets {     public static <T> Set<T> union(Set<T> a,Set<T> b){         Set<T> result = new HashSet<T>(a);         result.addAll(b);         return result;     }     public static <T> Set<T> intert(Set<T> a,Set<T> b){         Set<T> result = new HashSet<T>(a);         result.retainAll(b);         return result;     }     public static <T> Set<T> difference(Set<T> superset,Set<T> subset){         Set<T> result = new HashSet<T>(subset);         result.removeAll(subset);         return result;     }     public static <T> Set<T> complement(Set<T> a,Set<T> b){         return difference(union(a, b), intert(a, b));     }}

在前三个方法中,都将第一个参数Set复制了一份,将Set中的所有引用都存入一个新的HashSet对象中,因此,我们并未直接修改参数中的Set。返回值是一个全新的Set对象。这四个方法表达了一个数学集合的操作,union()返回一个Set,它将两个参数合并在一起,intert()返回的Set只包含两个参数共有的部分;difference()方法从superset中移除subset包含的元素;complement()返回的Set包含除了交集之外的所有元素。

0 0