Thinking in Java 第15章 泛型(15.5-15.19)

来源:互联网 发布:c语言自学需要什么软件 编辑:程序博客网 时间:2024/05/24 06:32

//声明:部分内容引自《Java编程思想(第四版)》机械工业出版社

【匿名内部类】

– 泛型可应用于内部类以及匿名内部类。

【擦除的神秘之处】

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

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

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

【擦除的补偿】

– 可以通过引入类型标签来对擦除进行补偿。
例:

package generics;//: generics/ClassTypeCapture.javaclass 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(ctt2.f(new Building()));    System.out.println(ctt2.f(new House()));  }} /* Output:truetruefalsetrue*///:~

编译器将确保类型标签可以匹配泛型参数。

【边界】

– 使用关键字 extends。

– Class must be first, then interfaces.

– As with inheritance, you can have only one concrete class but multiple interfaces.

【通配符(数组的协变性)】

– 数组的协变性 (covariant) 是指如果类 Base 是类 Sub 的基类,那么 Base[] 就是 Sub[] 的基类。而泛型是不可变的 (invariant),List< Base > 不会是 List< Sub > 的基类,更不会是它的子类。

– 捕获转换:如果向一个使用 < ? > 的方法传递原生类型,那么对于编译器来说,可能会推断出实际的类型参数,使得这个方法可以回转并调用另一个使用这个确切类型的方法。
例:

package generics;//: generics/CaptureConversion.javapublic class CaptureConversion {  static <T> void f1(Holder<T> holder) {    T t = holder.get();    System.out.println(t.getClass().getSimpleName());  }  static void f2(Holder<?> holder) {    f1(holder); // Call with captured type  }   @SuppressWarnings("unchecked")  public static void main(String[] args) {    Holder raw = new Holder<Integer>(1);    // f1(raw); // Produces warnings    f2(raw); // No warnings    Holder rawBasic = new Holder();    rawBasic.set(new Object()); // Warning    f2(rawBasic); // No warnings    // Upcast to Holder<?>, still figures it out:    Holder<?> wildcarded = new Holder<Double>(1.0);    f2(wildcarded);  }} /* Output:IntegerObjectDouble*///:~

【问题】

– 任何基本类型都不能作为类型参数。

– 自动包装机制不能应用于数组。

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

– 使用带有泛型类型参数的转型或 instanceof 不会有任何效果。

– 重载参数为泛型类型的函数时,由于擦除的原因,重载方法将产生相同的类型签名,因此编译失败。当被擦除的参数不能产生唯一的参数列表时,必须提供明显有区别的方法名。

【自限定的类型】

– 自限定:class A extends SelfBounded<A> {} 它可以保证类型参数必须与正在被定义的类相同。

– 自限定限制只能强制作用于继承关系。如果使用自限定,就应该了解这个类所用的类型参数将与使用这个参数的类具有相同的基类型。这会强制要求使用这个类的每个人都要遵循这种形式。

– 自限定类型可以产生斜边参数类型:方法参数类型会随子类而变化。

【动态类型安全】

– 解决旧式代码破坏容器的问题:使用静态方法 checkedCollection()、checkedList()、checkedMap()、checkedSet()、checkedSortedMap()、checkedSortedSet()。这些方法每一个都会将你希望动态检查的容器当作第一个参数接受,并将你希望强制要求的类型作为第二个参数接受。

– 将导出类型的对象放置到将要检查基类型的受检查容器中是没有问题的。

【异常】

– catch 语句不能捕获泛型类型的异常,因为在编译期和运行时都必须知道异常的确切类型。泛型类也不能直接或间接继承自 throwable。

– 可以编写随检查型异常的类型而发生变化的泛型代码。
例:

package generics;//: generics/ThrowGenericException.javaimport java.util.*;interface Processor<T,E extends Exception> {  void process(List<T> resultCollector) throws E;}class ProcessRunner<T,E extends Exception>extends ArrayList<Processor<T,E>> {  List<T> processAll() throws E {    List<T> resultCollector = new ArrayList<T>();    for(Processor<T,E> processor : this)      processor.process(resultCollector);    return resultCollector;  }}   class Failure1 extends Exception {}class Processor1 implements Processor<String,Failure1> {  static int count = 3;  public void  process(List<String> resultCollector) throws Failure1 {    if(count-- > 1)      resultCollector.add("Hep!");    else      resultCollector.add("Ho!");    if(count < 0)       throw new Failure1();  }}   class Failure2 extends Exception {}class Processor2 implements Processor<Integer,Failure2> {  static int count = 2;  public void  process(List<Integer> resultCollector) throws Failure2 {    if(count-- == 0)      resultCollector.add(47);    else {      resultCollector.add(11);    }    if(count < 0)       throw new Failure2();  }}   public class ThrowGenericException {  public static void main(String[] args) {    ProcessRunner<String,Failure1> runner =      new ProcessRunner<String,Failure1>();    for(int i = 0; i < 3; i++)      runner.add(new Processor1());    try {      System.out.println(runner.processAll());    } catch(Failure1 e) {      System.out.println(e);    }    ProcessRunner<Integer,Failure2> runner2 =      new ProcessRunner<Integer,Failure2>();    for(int i = 0; i < 3; i++)      runner2.add(new Processor2());    try {      System.out.println(runner2.processAll());    } catch(Failure2 e) {      System.out.println(e);    }  }} ///:~

运行结果

【混型】

– 混型:混合多个类的能力,以产生一个可以表示混型中所有类型的类。

– 与接口混合。

– 使用装饰器模式。装饰器模式使用分层对象来动态透明地向单个对象中添加责任。装饰器指定包装在最初的对象周围的所有对象都具有相同的基本接口。

– 与动态代理混合。通过使用动态代理,所产生的类的动态类型将会是已经混入的组合类型。由于动态代理的限制,每个被混入的类都必须是某个接口的实现。

【潜在类型机制】

– Java并不支持潜在类型机制,若强制实现,会被强制要求使用一个类或接口,并在边界表达式中指定它。

【对缺乏潜在类型机制的补偿】

– 反射。
例:

package generics;//: generics/LatentReflection.java// Using Reflection to produce latent typing.import java.lang.reflect.*;import static net.mindview.util.Print.*;// Does not implement Performs:class Mime {  public void walkAgainstTheWind() {}  public void sit() { print("Pretending to sit"); }  public void pushInvisibleWalls() {}  public String toString() { return "Mime"; }}// Does not implement Performs:class SmartDog {  public void speak() { print("Woof!"); }  public void sit() { print("Sitting"); }  public void reproduce() {}}   class CommunicateReflectively {  public static void perform(Object speaker) {    Class<?> spkr = speaker.getClass();    try {      try {        Method speak = spkr.getMethod("speak");        speak.invoke(speaker);      } catch(NoSuchMethodException e) {        print(speaker + " cannot speak");      }      try {        Method sit = spkr.getMethod("sit");        sit.invoke(speaker);      } catch(NoSuchMethodException e) {        print(speaker + " cannot sit");      }    } catch(Exception e) {      throw new RuntimeException(speaker.toString(), e);    }  }}public class LatentReflection {  public static void main(String[] args) {    CommunicateReflectively.perform(new SmartDog());    CommunicateReflectively.perform(new Robot());    CommunicateReflectively.perform(new Mime());  }} /* Output:Woof!SittingClick!Clank!Mime cannot speakPretending to sit*///:~

运行结果

– 使用 Iterable 接口。

– 如果没有碰巧拥有正确的接口:“可以在 Collection 的子类上调用 add()。”这样产生的代码并不是特别泛化,因为它必须限制为只能工作于 Collection 实现。
例:

package generics;//: generics/Fill.java// Generalizing the FilledList idea// {main: FillTest}import java.util.*;// Doesn't work with "anything that has an add()." There is// no "Addable" interface so we are narrowed to using a// Collection. We cannot generalize using generics in// this case.public class Fill {  public static <T> void fill(Collection<T> collection,  Class<? extends T> classToken, int size) {    for(int i = 0; i < size; i++)      // Assumes default constructor:      try {        collection.add(classToken.newInstance());      } catch(Exception e) {        throw new RuntimeException(e);      }  }}class Contract {  private static long counter = 0;  private final long id = counter++;  public String toString() {    return getClass().getName() + " " + id;  }}class TitleTransfer extends Contract {}class FillTest {  public static void main(String[] args) {    List<Contract> contracts = new ArrayList<Contract>();    Fill.fill(contracts, Contract.class, 3);    Fill.fill(contracts, TitleTransfer.class, 2);    for(Contract c: contracts)      System.out.println(c);    SimpleQueue<Contract> contractQueue =      new SimpleQueue<Contract>();    // Won't work. fill() is not generic enough:    // Fill.fill(contractQueue, Contract.class, 3);  }} /* Output:Contract 0Contract 1Contract 2TitleTransfer 3TitleTransfer 4*///:~

运行结果

– 用适配器仿真潜在类型机制。

【将函数对象用作策略】

– 函数对象:在某种程度上行为像函数的对象——一般地,会有一个相关的方法(在支持操作符重载的语言中,可以创建对这个方法的调用,而这个调用看起来就和普通的方法调用一样)。函数对象的价值就在于,与普通方法不同,它们可以传递出去,并且还可以拥有在多个调用之间持久化的状态。函数对象的主要目的是要创建某种事物,使它的行为就像是一个可以传递出去的单个方法一样,这样,它就和策略设计模式紧耦合了,有时甚至无法区分。
例:

package generics;//: generics/Functional.javaimport java.math.*;import java.util.concurrent.atomic.*;import java.util.*;import static net.mindview.util.Print.*;// Different types of function objects:interface Combiner<T> { T combine(T x, T y); }interface UnaryFunction<R,T> { R function(T x); }interface Collector<T> extends UnaryFunction<T,T> {  T result(); // Extract result of collecting parameter}interface UnaryPredicate<T> { boolean test(T x); }public class Functional {  // Calls the Combiner object on each element to combine  // it with a running result, which is finally returned:  public static <T> T  reduce(Iterable<T> seq, Combiner<T> combiner) {    Iterator<T> it = seq.iterator();    if(it.hasNext()) {      T result = it.next();      while(it.hasNext())        result = combiner.combine(result, it.next());      return result;    }    // If seq is the empty list:    return null; // Or throw exception  }  // Take a function object and call it on each object in  // the list, ignoring the return value. The function  // object may act as a collecting parameter, so it is  // returned at the end.  public static <T> Collector<T>  forEach(Iterable<T> seq, Collector<T> func) {    for(T t : seq)      func.function(t);    return func;  }  // Creates a list of results by calling a  // function object for each object in the list:  public static <R,T> List<R>  transform(Iterable<T> seq, UnaryFunction<R,T> func) {    List<R> result = new ArrayList<R>();    for(T t : seq)      result.add(func.function(t));    return result;  }  // Applies a unary predicate to each item in a sequence,  // and returns a list of items that produced "true":  public static <T> List<T>  filter(Iterable<T> seq, UnaryPredicate<T> pred) {    List<T> result = new ArrayList<T>();    for(T t : seq)      if(pred.test(t))        result.add(t);    return result;  }  // To use the above generic methods, we need to create  // function objects to adapt to our particular needs:  static class IntegerAdder implements Combiner<Integer> {    public Integer combine(Integer x, Integer y) {      return x + y;    }  }  static class  IntegerSubtracter implements Combiner<Integer> {    public Integer combine(Integer x, Integer y) {      return x - y;    }  }  static class  BigDecimalAdder implements Combiner<BigDecimal> {    public BigDecimal combine(BigDecimal x, BigDecimal y) {      return x.add(y);    }  }  static class  BigIntegerAdder implements Combiner<BigInteger> {    public BigInteger combine(BigInteger x, BigInteger y) {      return x.add(y);    }  }  static class  AtomicLongAdder implements Combiner<AtomicLong> {    public AtomicLong combine(AtomicLong x, AtomicLong y) {      // Not clear whether this is meaningful:      return new AtomicLong(x.addAndGet(y.get()));    }  }  // We can even make a UnaryFunction with an "ulp"  // (Units in the last place):  static class BigDecimalUlp  implements UnaryFunction<BigDecimal,BigDecimal> {    public BigDecimal function(BigDecimal x) {      return x.ulp();    }  }  static class GreaterThan<T extends Comparable<T>>  implements UnaryPredicate<T> {    private T bound;    public GreaterThan(T bound) { this.bound = bound; }    public boolean test(T x) {      return x.compareTo(bound) > 0;    }  }  static class MultiplyingIntegerCollector  implements Collector<Integer> {    private Integer val = 1;    public Integer function(Integer x) {      val *= x;      return val;    }    public Integer result() { return val; }  }  public static void main(String[] args) {    // Generics, varargs & boxing working together:    List<Integer> li = Arrays.asList(1, 2, 3, 4, 5, 6, 7);    Integer result = reduce(li, new IntegerAdder());    print(result);    result = reduce(li, new IntegerSubtracter());    print(result);    print(filter(li, new GreaterThan<Integer>(4)));    print(forEach(li,      new MultiplyingIntegerCollector()).result());    print(forEach(filter(li, new GreaterThan<Integer>(4)),      new MultiplyingIntegerCollector()).result());    MathContext mc = new MathContext(7);    List<BigDecimal> lbd = Arrays.asList(      new BigDecimal(1.1, mc), new BigDecimal(2.2, mc),      new BigDecimal(3.3, mc), new BigDecimal(4.4, mc));    BigDecimal rbd = reduce(lbd, new BigDecimalAdder());    print(rbd);    print(filter(lbd,      new GreaterThan<BigDecimal>(new BigDecimal(3))));    // Use the prime-generation facility of BigInteger:    List<BigInteger> lbi = new ArrayList<BigInteger>();    BigInteger bi = BigInteger.valueOf(11);    for(int i = 0; i < 11; i++) {      lbi.add(bi);      bi = bi.nextProbablePrime();    }    print(lbi);    BigInteger rbi = reduce(lbi, new BigIntegerAdder());    print(rbi);    // The sum of this list of primes is also prime:    print(rbi.isProbablePrime(5));    List<AtomicLong> lal = Arrays.asList(      new AtomicLong(11), new AtomicLong(47),      new AtomicLong(74), new AtomicLong(133));    AtomicLong ral = reduce(lal, new AtomicLongAdder());    print(ral);    print(transform(lbd,new BigDecimalUlp()));  }} /* Output:28-26[5, 6, 7]504021011.000000[3.300000, 4.400000][11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]311true265[0.000001, 0.000001, 0.000001, 0.000001]*///:~

运行结果

【总结】

0 0