Java编程思想--15泛型(未看完)

泛型使得我们可以编写更为通用的代码,使得代码能够应用于“某种不具体的类型”,而非使用具体的接口和类。

15.1 与C++的比较

Java泛型较弱。需要着重关注泛型的边界。(理解边界所在才能成为编程高手)

15.2 简单泛型

容器类

可以通过上转型使引用持有不同的对象。但是通常希望容器持有不确定的某一类对象。泛型的主要目的就是指定容器应该持有什么类型的对象,而且由编译器来保证其正确性。

15.2.1 一个元组类库

通常一个方法只能返回单个对象,但是通过元组可以将返回值进行打包,将返回值一次性解决。

package chapter15.tuple;

public class TwoTuple<A, B> {
    public final A a;
    public final B b;
    public TwoTuple(A a, B b){
        this.a = a;
        this.b = b;
    }
    public String toString(){
        return "("+a+","+b+")";
    }
package chapter15;

import chapter15.tuple.TwoTuple;

public class TupleTest {
    public static TwoTuple<String, Integer> f(){
        return new TwoTuple<String, Integer>("3", 2);
    }
    public static void main(String[] args) {
        TwoTuple<String, Integer> two = f();
        System.out.println(two.toString());
    }
}

15.2.2 一个堆栈类

package chapter15;

public class LinkedStack<T> {
    private class Node<N>{
        N item;
        Node<N> next;
        Node(){
            item = null;
            next = null;
        }
        Node(N item, Node<N> next){
            this.item = item;
            this.next = next;
        }
        boolean end(){
            return item == null && next == null;
        }
    }
    private Node<T> top = new Node<T>();   //末端哨兵
    public void push(T item){
        top = new Node<T>(item, top); 
    }
    public T pop(){
        T rst = top.item;
        if(!top.end())
            top = top.next;
        return rst;
    }

    public static void main(String[] args) {
        LinkedStack<String> ls = new LinkedStack<>();
        for(String s : "We are the Champion".split(" "))
            ls.push(s);
        String s;
        while((s=ls.pop()) != null)
            System.out.println(s);
    }
}

15.2.3 RandomList

package chapter15;

import java.util.ArrayList;
import java.util.Random;

public class RandomList<T> {
    private ArrayList<T> storage = new ArrayList<>();
    private Random rand = new Random(0);
    public void add(T item){    storage.add(item);  }
    public T select(){
        return storage.get(rand.nextInt(storage.size()));
    }
    public static void main(String[] args) {
        RandomList<String> rl = new RandomList<>();

        for(String tmp: "We are the champion!".split(" "))
            rl.add(tmp);
        
        for(int i = 0; i< 10; i++){
            String s = rl.select();
            System.out.println(s);
        }
    }
}

15.3 泛型接口

泛型也能应用于接口,如生成器Generator。但是无法基本数据类型作为参数。

public interface Generator<T> {
    T next();
}

15.4 泛型方法

泛型方法可以独立于泛型类,无论何时,如果能用泛型方法,就应当尽量使用泛型方法。

15.4.1 杠杆利用类型参数推断

参数推断仅在赋值操作时发生

package chapter15;

import java.util.*;

public class New<T> {
    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> List<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 void f(T obj){
        return;
    }
    public static void main(String[] args) {
        Map<String, String> m = New.map();
        // f(New.llist()); // Do not compile
    }
}

15.4.2 可变参数与类型方法

可变参数与泛型方法可以很好地共存。

15.4.3 用于Generator的泛型方法

import java.util.*;

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

15.4.4 一个通用的Generator

package chapter15.generics;

public class BasicGenerator<T> implements Generator {
    private Class<T> type;

    public BasicGenerator(Class<T> t){type=t;}
    @Override
    public Object next() {
        try {
            return type.newInstance();
        } catch (Exception e) {
            //TODO: handle exception
            throw new RuntimeException();
        }
        
    }
    public static <T> Generator<T> create(Class<T> type){
        return new BasicGenerator<>(type);
    }
}
package chapter15.generics;

public class CountObject {
    private static long counter = 0;
    private static final long id = counter++;
    public long id(){
        return id;
    }
    public String toString(){
        return "Counter Object " + id;
    }
    public static void main(String[] args) {
        Generator<CountObject> gen =
          BasicGenerator.create(CountObject.class);
        for(int i = 0; i < 5; i++)
          System.out.println(gen.next());
      }
}

15.4.5 简化元组的使用

使用函数重载与泛型,简化元组的使用。

package chapter15.tuple;

public class Tuple {
    public static <A, B> TwoTuple<A, B> tuple(A a, B b){
        return new TwoTuple<A, B>(a, b);
    }
    public static <A, B, C> ThreeTuple<A, B, C> tuple(A a, B b, C c){
        return new ThreeTuple<A,B,C>(a, b, c);
    }
    public static <A, B, C, D> FourTuple<A, B, C, D> tuple(A a, B b, C c, D d){
        return new FourTuple<A,B,C,D>(a, b, c, d);
    }
}
public class TupleTest2 {
    static TwoTuple<String, Integer> f(){
        return Tuple.tuple("hi", 47);
    }
    public static void main(String[] args) {
        System.out.println(f());
    }
}

14.5.6 一个Set实用工具

import java.util.*;

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> intersection(Set<T> a, Set<T> b) {
    Set<T> result = new HashSet<T>(a);
    result.retainAll(b);
    return result;
  }	
  // Subtract subset from superset:
  public static <T> Set<T>
  difference(Set<T> superset, Set<T> subset) {
    Set<T> result = new HashSet<T>(superset);
    result.removeAll(subset);
    return result;
  }
  // Reflexive--everything not in the intersection:
  public static <T> Set<T> complement(Set<T> a, Set<T> b) {
    return difference(union(a, b), intersection(a, b));
  }
} ///:~

15.5 匿名内部类

15.6 构建复杂模型

使用泛型的重要好处是能够简单而安全的构建复杂的模型。

import java.util.ArrayList;

import chapter15.tuple.*;
public class TupleList <A, B, C, D> extends ArrayList<FourTuple<A, B, C, D>>{
    public static void main(String[] args) {
        TupleList<String, Integer, String, Integer> tl = new TupleList<>();
        tl.add(TupleTest.h());
        tl.add(TupleTest.h());
        tl.add(TupleTest.h());

        for(FourTuple<String, Integer, String, Integer> i: tl)
            System.out.println(i);
    }
}

15.7 擦除的神秘之处

两个展现神秘之处的例子:
例子1:ArrayList<String>与ArrayList<Integer>的类型是相同的。

//: generics/ErasedTypeEquivalence.java
import java.util.*;
public class ErasedTypeEquivalence {
  public static void main(String[] args) {
    Class c1 = new ArrayList<String>().getClass();
    Class c2 = new ArrayList<Integer>().getClass();
    System.out.println(c1 == c2);
  }
} /* Output:
true
*///:~

尽管在程序员看来,ArrayList<String>与ArrayList<Integer>的类型应该是不同的,但是在实际运行中,它们被视为同一类型。

例子2:丢失的类型信息。

package chapter15;

import java.util.*;

class Frob {}
class Fnorkle {}
class Quark<Q> {}
class Particle<POSITION,MOMENTUM> {}

public class LostInformation {
  public static void main(String[] args) {
    List<Frob> list = new ArrayList<Frob>();
    Map<Frob,Fnorkle> map = new HashMap<Frob,Fnorkle>();
    Quark<Fnorkle> quark = new Quark<Fnorkle>();
    Particle<Long,Double> p = new Particle<Long,Double>();
    System.out.println(Arrays.toString(
      list.getClass().getTypeParameters()));
    System.out.println(Arrays.toString(
      map.getClass().getTypeParameters()));
    System.out.println(Arrays.toString(
      quark.getClass().getTypeParameters()));
    System.out.println(Arrays.toString(
      p.getClass().getTypeParameters()));
  }
} /* Output:
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]
*///:~

尽管在使用时确定了泛型信息,但是在获取参数类型是,返回的仍是原始类型,类型信息被擦除了。

在Java中一个残酷的现实是:在泛型代码内部,无法获得有关泛型参数类型的信息。

15.7.1 C++ 的方式

下面是两个例子,分别展示C++与Java在通过泛型调用对象方法时的行为。

  1. C++b版
//: generics/Templates.cpp
#include <iostream>
using namespace std;

template<class T> class Manipulator {
  T obj;
public:
  Manipulator(T x) { obj = x; }
  void manipulate() { obj.f(); }
};

class HasF {
public:
  void f() { cout << "HasF::f()" << endl; }
};

int main() {
  HasF hf;
  Manipulator<HasF> manipulator(hf);
  manipulator.manipulate();
} /* Output:
HasF::f()
///:~

C++在编译时会进行检查,查看方法中是否存在HasF方法,若没有会产生一个编译时期的错误,这样类型安全就得到了保障。

  1. Java版
public class HasF {
    public void f(){ System.out.println("I am f()");}
}
class Manipulator<T> {
    private T obj;
    public Manipulator(T x) { obj = x; }
    // 编译时会报错
    public void manipulate() { obj.f(); }
  }

  public class Manipulation {
    public static void main(String[] args) {
      HasF hf = new HasF();
      Manipulator<HasF> manipulator =
        new Manipulator<HasF>(hf);
      manipulator.manipulate();
    }
} ///:~

Java编译器无法将manipulate()必须能够在obj上调用f()这一条件映射到HasF拥有f()这一事实上。为了调用f()必须使用泛型类,给泛型类定边界。

class Manipulator2<T extends HasF> {
    private T obj;
    public Manipulator2(T x) { obj = x; }
    public void manipulate() { obj.f(); }
} ///:~

使用extends关键字来声明T必须拥有具体类型HasF或者从HasF导出的类型。

应当注意的是,在使用泛型时,应当确保它复杂到必须使用泛型的程度。如上述代码段可以轻松的切换到具体类型。

class Manipulator3 {
    private HasF obj;
    public Manipulator2(HasF x) { obj = x; }
    public void manipulate() { obj.f(); }
} ///:~

15.7.2 迁移兼容性

Java为了支持向后兼容性(JAVA SE5之前没有泛型)。如果你在泛型的代码里使用了没有使用泛型的代码,那么便会出现问题。擦除机制可以非泛化代码与泛化代码共存。

15.7.3 擦除的问题

  1. 泛型无法应用于显式的运行时类型操作之中,例如转型、instanceof操作和new表达式。因为所有参数信息都丢失了。在编写代码时应注意,你只是看起来拥有类型信息而已,不过在根本上仍是Object。
class GenericBase<T> {
    private T element;
    public void set(T arg) { arg = element; }
    public T get() { return element; }
}
  
class Derived1<T> extends GenericBase<T> {}
class Derived2 extends GenericBase {} // No warning
// class Derived3 extends GenericBase<?> {}
// Strange error:
//   unexpected type found : ?
//   required: class or interface without bounds	

public class ErasureAndInheritance {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) {
      Derived2 d2 = new Derived2();
      Object obj = d2.get();
      d2.set(obj); // Warning here!
    }
} ///:~

Derived2 对泛型类进行了继承,但是没有发出任何警告,警告在调用set时才发出(可以用SUpressWarning(“unchecked”)抑制,避免宽泛的警告遮蔽掉重要的问题)。

15.7.4 边界处的动作

泛型中所有的动作都发生在边界处,即对传进来的值进行额外的编译期检查,并插入对传出去值的转型。边界就是发生动作的地方

15.8 擦除的补偿

  1. 无法使用运行时的类型检查的问题。解决方案:可以调用动态的isInstance()方法。
//: generics/ClassTypeCapture.java

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 =  ClassTypeCapture<House>(House.class);
    System.out.println(ctt2.f(new Building()));
    System.out.println(ctt2.f(new House()));
  }
} /* Output:
true
true
false
true
*///:~

15.8.2

  1. 无法使用new创建实例。解决方案:构建工厂对象。
class ClassAsFactory<T> {
  T x;

  public ClassAsFactory(Class<T> kind) {
    try {
      x = kind.newInstance();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
}

class Employee {
}

public class InstantiateGenericType {
  public static void main(String[] args) {
    ClassAsFactory<Employee> fe = new ClassAsFactory<Employee>(Employee.class);
    System.out.println("ClassAsFactory<Employee> succeeded");
    try {
      ClassAsFactory<Integer> fi = new ClassAsFactory<Integer>(Integer.class);
    } catch (Exception e) {
      System.out.println("ClassAsFactory<Integer> failed");
    }
  }
}
/*
 * Output: ClassAsFactory<Employee> succeeded ClassAsFactory<Integer> failed
 */// :~

但是上述工厂会出问题,例如Integer没有默认的构造器。Sun推荐构造显示的工厂类。有两种方式:

  1. 工厂类实现创建的接口
package chapter15;

//: generics/FactoryConstraint.java

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)使用模版方法设计模式
在下面的例子中,get()是模版方法,而create()是在子类中定义的产生子类对象的方法。

15.8.2 泛型数组

暂时pass

15.9 边界

使用边界可以限制泛型的参数类型,使得泛型强制使用泛型可以规定的类型,但是更潜在的因素是希望可以按照自己的边界类型来调用方法。为了执行这种限制,Java冲用了extends关键字。

package chapter15;

interface HasColor {
    java.awt.Color getColor();
}

class Colored<T extends HasColor> {
    T item;

    Colored(T item) {
        this.item = item;
    }

    T getItem() {
        return item;
    }

    // The bound allows you to call a method:
    java.awt.Color color() {
        return item.getColor();
    }
}

class Dimension {
    public int x, y, z;
}

// This won't work -- class must be first, then interfaces:
// class ColoredDimension<T extends HasColor & Dimension> {

// Multiple bounds:
class ColoredDimension<T extends Dimension & HasColor> {
    T item;

    ColoredDimension(T item) {
        this.item = item;
    }

    T getItem() {
        return item;
    }

    java.awt.Color color() {
        return item.getColor();
    }

    int getX() {
        return item.x;
    }

    int getY() {
        return item.y;
    }

    int getZ() {
        return item.z;
    }
}

interface Weight {
    int weight();
}

// As with inheritance, you can have only one
// concrete class but multiple interfaces:
class Solid<T extends Dimension & HasColor & Weight> {
    T item;

    Solid(T item) {
        this.item = item;
    }

    T getItem() {
        return item;
    }

    java.awt.Color color() {
        return item.getColor();
    }

    int getX() {
        return item.x;
    }

    int getY() {
        return item.y;
    }

    int getZ() {
        return item.z;
    }

    int weight() {
        return item.weight();
    }
}

class Bounded extends Dimension implements HasColor, Weight {
    public java.awt.Color getColor() {
        return null;
    }

    public int weight() {
        return 0;
    }
}

public class BasicBounds {
    public static void main(String[] args) {
        Solid<Bounded> solid = new Solid<Bounded>(new Bounded());
        solid.color();
        solid.getY();
        solid.weight();
    }
} /// :~

也可以通过继承的方式拓展层次结构。因而不必在

package chapter15;

class HoldItem<T> {
    T item;
    HoldItem(T item) { this.item = item; }
    T getItem() { return item; }
  }
  
  class Colored2<T extends HasColor> extends HoldItem<T> {
    Colored2(T item) { super(item); }
    java.awt.Color color() { return item.getColor(); }
  }
  
  class ColoredDimension2<T extends Dimension & HasColor>
  extends Colored2<T> {
    ColoredDimension2(T item) {  super(item); }
    int getX() { return item.x; }
    int getY() { return item.y; }
    int getZ() { return item.z; }
  }
  
  class Solid2<T extends Dimension & HasColor & Weight>
  extends ColoredDimension2<T> {
    Solid2(T item) {  super(item); }
    int weight() { return item.weight(); }
  }
  
  public class InheritBounds {
    public static void main(String[] args) {
      Solid2<Bounded> solid2 =
        new Solid2<Bounded>(new Bounded());
      solid2.color();
      solid2.getY();
      solid2.weight();
    }
} ///:~

15.10 通配符

数组协变,暂时Pass

15.11 问题

15.11.1 基本类型不能作为类型参数

可以使用包装类。

15.11.2 泛型化接口

一个类不能实现同一个泛型接口的两种变体。由于擦除原因,这两个变体会成为相同的接口。

interface Payable<T> {}
class Employee implements Payable<Employee> {}
class Hourly extends Employee implements Payable<Hourly> {} ///:~

15.11.3 转型和警告

使用带有泛型类型参数的转型或者instanceof不会有任何效果。如下面的容器在内部将各个值存储为Object,在获取这些值时再将它们转型为T。

package chapter15;

//: generics/GenericCast.java

class FixedSizeStack<T> {
  private int index = 0;
  private Object[] storage;

  public FixedSizeStack(int size) {
    storage = new Object[size];
  }

  public void push(T item) {
    storage[index++] = item;
  }

  @SuppressWarnings("unchecked")
  public T pop() {
    return (T) storage[--index];
  }
}

public class GenericCast {
  public static final int SIZE = 10;

  public static void main(String[] args) {
    FixedSizeStack<String> strings = new FixedSizeStack<String>(SIZE);
    for (String s : "A B C D E F G H I J".split(" "))
      strings.push(s);
    for (int i = 0; i < SIZE; i++) {
      String s = strings.pop();
      System.out.print(s + " ");
    }
  }
} /*
   * Output: J I H G F E D C B A
   */// :~

pop()方法并没有执行真正的转型。由于擦除原因,T被擦除到它的第一个边界Object。

15.11.4 重载

下列重载无法编译。由于擦除原因,重载将产生相同类型的签名。不过可以通过第一不同的方法名来解决。

import java.util.*;
public class UseList<W,T> {
  void f(List<T> v) {}
  void f(List<W> v) {}
} ///:~

15.11.5 基类劫持了接口

无法进行窄化。

public class ComparablePet
implements Comparable<ComparablePet> {
  public int compareTo(ComparablePet arg) { return 0; }
} ///:~
class Cat extends ComparablePet implements Comparable<Cat>{
    // Error: Comparable cannot be inherited with
    // different arguments: <Cat> and <Pet>
    public int compareTo(Cat arg) { return 0; }
} ///:~

在这里插入图片描述

15.12 自限定的类型

class SelfBounded<T extends SelfBounded<T>>{ //...

15.2.1 古怪的循环泛型

下面的程序可以理解为:我创建了一个新类,该类继承了一个泛型,该泛型类接受该类的名字作为其参数。

class GenericType<T> {}
public class CiriousRecurringGeneric 
	extends GenericType<CiriousRecurringGeneric>{}

15.2.2 自限定

BasicHolder可以将任何类型作为其泛型参数。

class Other{}
class BasicOther extends BasicHolder<other>{}

自限定将采取额外的步骤,强制泛型当作自己的边界参数来使用

pass

15.13 动态类型安全

因为可以向Java SE5之前的代码传递泛型容器,所以旧式代码仍旧有可能会破坏你的容器。

15.14 异常

由于擦除的原因,将泛型应用于异常是非常受限的。catch语句不能不火泛型类的异常,这是因为在编译器和运行时都必须知道异常的确切类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值