incendi0 / Lumos

好记性不如烂笔头。A very handy charm that lights the tip of your wand in case. It’s like the flashlight on your iPhone, except magic. An added bonus spell is Lumos Maxima, which allows the bulb of light to fling into the sky and provide light all around a dark room, for example.
0 stars 0 forks source link

Java Generics and collections #2

Open incendi0 opened 4 years ago

incendi0 commented 4 years ago

// TODO

incendi0 commented 4 years ago

Chapter 2. Subtyping and Wildcards

2.1 Subtyping and the Substitution Principle

Subtyping具有传递性。A是B的子类,B是C的子类,A也是C的子类。引用类型都是Object的子类,类型也是自己的子类。

Substitution Principle: a variable of a given type may be assigned a value of any subtype of that type, and a method with a parameter of a given type may be invoked with an argument of any subtype of that type.

泛型是invariant(不变),List\<Integer>和List\<Number>没有父类子类的关系,当然List\<Integer>是Collection\<Integer>的子类;数组有covariant(协变)的性质,Integer[]是Number[]的子类。

如果想要List表现出类似数组的父子类关系,可以使用wildcards(通配符)。

2.2 Wildcards with extends

interface Collection<E> {
    boolean addAll(Collection<? extends E> c);
}

?被称为通配符,? extends E表示E的任何一个子类。

List<Integer> ints = new ArrayList<>();
ints.add(1);
// List<Number> nums = ints; // wrong!
List<? extends Number> nums = ints; // OK, List<Integer>是List<? extends Number>的子类
nums.add(3.14); // wrong!

原则上,对于包含具有? extends E类型的类(结构体),只能从结构体取值,不能把值放入结构体,除非null。

2.3 Wildcards with super

public static <T> void copy(List<? super T> dst, List<? extends T> src) {
    for (int i = 0; i < src.size(); i++) {
        dst.set(i, src.get(i));
    }
}

? super T表示T的任何一个父类。

类似的,对于包含具有? super T类型的类(结构体),不能从结构体取值(除非Object),只能把值放入结构体。

2.4 The Get and Put Principle

The Get and Put Principle: use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don’t use a wildcard when you both get and put.

又称为PECS,即producers extends, consumers super,参照2.3中的代码示例。

String类型是final的,意味着无法继承String类,但是List\<String>并不等同于List\<? extends String>,事实上,前者是后者的子类。The Get and Put Principle告诉我们,我们可以把String加到List\<String>,但不能加到List\<? extends String>,所以两者类型不同,同时,当需要后者的时候,我们总能传入前者类型的值,所以前者是后者的子类。

2.5 Arrays

Integer[] ints = new Integer[] {1, 2, 3};
Number[] nums = ints; // OK,因为nums是ints的子类
nums[2] = 3.14; // wrong! ArrayStoreException,因为当一个数组第一次被分配的时候,已经被标记上具化(reified)的类型,此处是Integer,数组每次赋值的时候检查发现具化类型和赋值类型不兼容(double不能存入Integer数组中),就抛出ArrayStoreException

泛型是不变的,数组是协变的。

通配符为泛型引入了协变(covariant),S是T的子类,List\<S>是List\<? extends T>的子类。

通配符为泛型引入了逆变(contravariant),S是T的父类,List\<S>是List\<? super T>的子类。

泛型可以把问题暴露在编译器,数组的问题暴露在运行期

前者的优势:

  1. 问题及早暴露;
  2. 运行时不需要时刻去检查类型是否兼容;
  3. 集合类型多,API丰富。

选择后者的场景:

  1. 原生类型效率高,不需要装箱,同时也不会做类型检查,因为原生类型没有子类;
  2. 性能好

为什么要把数组设计成协变的呢?

在泛型出现之前是有必要的,比如你想写一个方法,但是所有的类型都能用,比如下面的sort和fill方法。

public static void sort(Object[] a);
public static void fill(Object[] a, Object val);

2.6 Wildcards Versus Type Parameters

interface Collection<E> {
    boolean contains(Objecat o);
    boolean containsAll(Collection<?> c);
}

Collection\<?>表示Collection\<? extends Object>

interface Collection<E> {
    boolean contains(E o);
    boolean containsAll(Collection<? extends E> c);
}

2.7 Wildcard Capture

public static void reverse(List<?> list) {
    List<Object> tmp = new ArrayList<Object>(list);
    for (int i = 0; i < list.size(); i++) {
        list.set(i, tmp.get(list.size()-i-1)); // compile-time error
    }
}
public static void reverse(List<?> list) { 
    rev(list); 
}
private static <T> void rev(List<T> list) {
    List<T> tmp = new ArrayList<T>(list);
    for (int i = 0; i < list.size(); i++) {
        list.set(i, tmp.get(list.size()-i-1));
    }
}   

另外一个原因需要知道通配符捕捉的原因是编译器报错,通配符表示未知类型,编译器可能会打印出类似capture of ?的错误信息,比如上面错误代码的编译错误。

2.8 Restrictions on Wildcards

通配符不能出现在类实例化顶层表达式(new),应用The Get and Put Principle,我们只能往这个类get值(extend)或者put值(super),但是类一般我们是要同时做到put和get。

List<?> list = new ArrayList<?>(); // compile-time error
Map<String, ? extends Number> map = new HashMap<String, ? extends Number>(); // compile-time error
List<List<?>> lists = new ArrayList<List<?>>();
lists.add(Arrays.asList(1,2,3));
lists.add(Arrays.asList("four","five"));
assert lists.toString().equals("[[1, 2, 3], [four, five]]");

通配符不能出现在泛型方法调用的时候

class Lists {
    public static <T> List<T> factory() { return new ArrayList<T>(); }
}
List<?> list = Lists.factory(); // OK
List<?> list = Lists.<Object>factory(); // OK
List<?> list = Lists.<?>factory(); // compile-time error
List<List<?>> = Lists.<List<?>>factory(); // ok, nested wildcards are permitted

通配符不能出现在implements或者extends的时候,原因是子类实例化需要调用父类构造函数,所以实例化的限制也会出现。

class AnyList extends ArrayList<?> {...} // compile-time error
class AnotherList implements List<?> {...} // compile-time error
class NestedList extends ArrayList<List<?>> {...} // ok
incendi0 commented 4 years ago

Chapter 3. Comparison and Bounds

3.1 Comparable

Consistent with Equals

x.equals(y) if and only if x.compareTo(y) == 0

有个例外是BigDecimal,4.0和4.00值(natural ordering)相同,但是精度不同(not consistent with Equals)。

Contract for Comparable

a. anti-symmetric, sgn(x.compareTo(y)) == -sgn(y.compareTo(x))

b. transitive, if x.compareTo(y) < 0 and y.compareTo(z) < 0 then x.compareTo(z) < 0

c. congruence, if x.compareTo(y) == 0 then sgn(x.compareTo(z)) == sgn(y.compareTo(z))

d. comparison be compatible with equality, x.equals(y) if and only if x.compareTo(y) == 0

e. reflexive, x.compareTo(x) == 0

注意compareTo的返回值,-1、0和1,同时比较Integer的时候不要直接做减法,可能会溢出。

3.2 Maximum of a Collection

T extends Comparable\<T>声明了类型变量T,且T被Comparable\<T>限定,类型限定只能用extends,不能用super。同时类型限定是递归的,即T的类型限定以来于T。

3.4 Comparator

The ordering provided by the Comparable interface is called the natural ordering, so the Comparator interface provides, so to speak, an unnatural ordering.

3.5 Enumerated Types

枚举类型会被展开为特定格式的类,类是单例的,枚举常量被限定为一个合适的static final 变量。

enum Season {
    Winter, Spring, Summer, Fall
}
// 枚举基类
public abstract class Enum<E extends Enum<E>> implements Comparable<E> {
    private final String name;
    private final int ordinal;
    protected Enum(String name, int ordinal) {
        this.name = name; 
        this.ordinal = ordinal;
    }
    public final String name() { return name; }
    public final int ordinal() { return ordinal; }
    public String toString() { return name; }
    public final int compareTo(E o) {
        return ordinal - o.ordinal;
    }
}
// Season类被展开为如下
final class Season extends Enum<Season> {
    private Season(String name, int ordinal) { super(name,ordinal); }
    public static final Season WINTER = new Season("WINTER", 0);
    public static final Season SPRING = new Season("SPRING", 1);
    public static final Season SUMMER = new Season("SUMMER", 2);
    public static final Season FALL = new Season("FALL", 3);
    private static final Season[] VALUES = { WINTER, SPRING, SUMMER, FALL };
    public static Season[] values() { return VALUES.clone(); }
    public static Season valueOf(String name) {
        for (Season e : VALUES) {
            if (e.name().equals(name)) {
                return e;
            } 
        }
        throw new IllegalArgumentException();
    }
}
public abstract class Enum<E extends Enum<E>> implements Comparable<E>
// 保证只能和Season类型枚举比较
class Enum<Season extends Enum<Season>> implements Comparable<Season>

3.8 Covariant Overriding

子类方法中参数类型一致,返回值可以是子类。

incendi0 commented 4 years ago

Chapter 4. Declarations

4.1 Constructors

调用泛型的构造函数时,不要忘记\<>,否则就变成raw type了。

4.2 Static Members

泛型被编译器擦除,运行时去掉了类型变量。

List<Integer> ints = Arrays.asList(1,2,3);
List<String> strings = Arrays.asList("one","two");
assert ints.getClass() == strings.getClass();

类型擦除的一个后果就是泛型类的静态变量不能引用泛型类的类型参数,因为静态变量是可以被不同类型实例化的类引用,同时,调用一个静态变量,类名不能是参数化的。

Cell.getCount(); // ok
Cell<Integer>.getCount(); // compile-time error
Cell<?>.getCount(); // compile-time error

4.3 Nested Classes

外部类的类型参数对嵌套类是可见的。

4.4 How Erasure Works

去掉参数化类型的类型参数,将类型变量替换成限定类型的擦除类型、如果没有限定就是Object。

  1. List\<Integer>、List\<String>和List\<List\<String>>的擦除都是List;
  2. List\<Integer>[]的擦除是List[];
  3. List以及其他raw type的擦除也是自身;
  4. int以及其他原生类型的擦除就是自身;
  5. Integer以及其他没有类型参数的类的擦除就是自身;
  6. 没有限定的类型参数T的擦除就是Object;
  7. T extends Comparable\<? super T>的类型擦除就是Comparable;
  8. LinkedCollection\<E>.Node的擦除是LinkedCollection.Node.
incendi0 commented 4 years ago

Chapter 6. Reification

To convert mentally into a thing; to materialize.

计算机中,具化表示类型的显式表示,即运行时类型信息。

6.1 Reifiable Types

In Java, the type of an array is reified with its component type, while the type of a parameterized type is reified without its type parameters.

a type is reifiable if the type is completely represented at run time,如下:

  1. primitive type,比如int;
  2. 非参数化的类或者接口,比如Integer,Runnable;
  3. 所有类型参数都是无限定的通配符的参数化类型,比如List\<?>,Map\<?, ?>;
  4. raw type,比如List,Map;
  5. 元素类型是reifiable的数组,比如int[],List\<?>[]

如下则不是reifiable类型:

  1. 类型变量,比如T;
  2. 有实参的参数化类型,比如List\<Integer>;
  3. 类型限定的参数化类型,比如List\<? extends Number>,List\<? extends Number>

6.2 Instance Tests and Casts

instance test和cast都依赖运行时检查类型,因此依赖具化,因此,对一个不是reifiable类型进行instance test会产生错误,类型转换则会产生警告。

c instanceof List<E>; // compile error
Iterator<E> it2 = ((List<E>)o).iterator(); // unchecked cast

c instanceof List<?>; // OK
(List<String>)(List<?>)objs; // unchecked cast

6.3 Exception Handling

class ParametricException<T> extends Exception {} // compile-time error

6.4 Array Creation

Because arrays must reify their component types, it is an error to create a new array unless its component type is reifiable.

T[] a = new T[c.size()]; // compile-time error, generic array creation

Inability to create generic arrays is one of the most serious restrictions in Java.

6.5 The Principle of Truth in Advertising

下列代码尝试把collection转化成数组

public static <T> T[] toArray(Collection<T> c) {
    T[] a = (T[])new Object[c.size()]; // unchecked cast
    int i=0; for (T x : c) a[i++] = x;
    return a;
}
public static void main(String[] args) {
    List<String> strings = Arrays.asList("one","two");
    String[] a = toArray(strings); // class cast error
}
// Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object;

上述代码中,数组的具化类型是[Ljava.lang.Object,元素类型是Object,可以通过反编译得到类型擦除后的代码

class Wrong {
    public static Object[] toArray(Collection c) {
        Object[] a = (Object[])new Object[c.size()]; // unchecked cast
        int i=0; for (Object x : c) a[i++] = x;
        return a;
    }
    public static void main(String[] args) {
        List strings = Arrays.asList(args);
        String[] a = (String[])toArray(strings); // class cast error
    }
}

不能把Object[]转换为String[],因此报错了。

The Principle of Truth in Advertising: the reified type of an array must be a subtype of the erasure of its static type.

generics for Java are accompanied by a cast-iron guarantee: no cast inserted by erasure will fail, so long as there are no unchecked warnings. The preceding principle illustrates the converse: if there are unchecked warnings, then casts inserted by erasure may fail.

解决方法:

  1. 要得到相同的类型,首先我们要知道这个类型

    class Right {
       public static <T> T[] Array(toCollection<T> c, T[] a) {
           if (a.length < c.size())
           a = (T[])java.lang.reflect.Array. // unchecked cast
           newInstance(a.get Class().getComponentType(), c.size());
           int i=0; for (T x : c) a[i++] = x;
           if (i < a.length) a[i] = null;
           return a;
       }
       public static void main(String[] args) {
           List<String> strings = Arrays.asList("one", "two");
           String[] a = toArray(strings, new String[0]);
           assert Arrays.toString(a).equals("[one, two]");
           String[] b = new String[] { "x","x","x","x" };
           toArray(strings, b);
           assert Arrays.toString(b).equals("[one, two, null, x]");
       }
    }

    newInstance返回的是Object,而不是Object[],原因在于可以创建原生类型数组,比如int[],int[]是Object的子类,而不是Object[]的子类,当然此处都是引用类型,只是要注意。

  2. 使用Class\<T>

    class RightWithClass {
       public static <T> T[] toArray(Collection<T> c, Class<T> k) {
           T[] a = (T[])java.lang.reflect.Array. // unchecked cast
           newInstance(k, c.size());
           int i=0; for (T x : c) a[i++] = x;
           return a;
       }
       public static void main(String[] args) {
           List<String> strings = Arrays.asList("one", "two");
           String[] a = toArray(strings, String.class);
           assert Arrays.toString(a).equals("[one, two]");
       }
    }

6.6 The Principle of Indecent Exposure

Principle of Indecent Exposure: never publicly expose an array where the components do not have a reifiable type.

incendi0 commented 4 years ago

Chapter 7. Reflection

7.1 Generics for Reflection

if T is a type without type parameters, then T.class has type Class, and if e is an expression of type T then e.getClass( ) has type Class<? extends T>.

Class<Integer> ki = Integer.class;
Number n = new Integer(42);
Class<? extends Number> kn = n.getClass();
assert ki == kn;
class Class<T> {
    public T newInstance(); // 返回新实例
    public T cast(Object o); // 转换为T类型,如果不成功抛出cast exception
    public Class<? super T> getSuperclass(); // 返回超类
    public <U> Class<? extends U> asSubclass(Class<U> k); // 类型转换,失败抛出cast exception
    public <A extends Annotation> A getAnnotation(Class<A> k); // 编译器类型检查,运行时不会抛出cast exception
    public boolean isAnnotationPresent(Class<? extends Annotation> k);
}

7.2 Reflected Types are Reifiable Types

If you try to reflect a parameterized type, you get the reified information for the corresponding raw type:

In general, if expression e has type T, then the expression e.getClass() has type Class<? extends |T|>, where |T| is the erasure of the type T.

List<Integer> ints = new ArrayList<>;
Class<? extends List> k = ints.getClass();
assert k == ArrayList.class;

对于class token,必须使用raw type,List.class可以,List\<?>.class不可以,尽管List\<?>也是reifiable type。

7.3 Reflection for Primitive Types

Java的所有类型,包括原生类型和数组类型,都有类字面量和对应的class token。

特殊的,int的类字面量是int.class,但是class token不能是Class\<int>,因为int不是引用类型,事实上,int的class token是Class\<Integer>,但是int.class.cast(o)和int.class.newInstance()并不返回Integer,而是抛出异常,类似的java.lang.reflect.Array.newInstance(int.class, size)也不会返回Integer[],而是int[]。这是很有争议的一点,这些例子看起来我们应该给予int.class的class token为Class\<?>,虽然事实上并不如此。

7.4 A Generic Reflection Library

class GenericReflection {
    public static <T> T newInstance(T obj)
            throws InstantiationException,
            IllegalAccessException,
            InvocationTargetException,
            NoSuchMethodException {
        Object newobj = obj.getClass().getConstructor().newInstance();
        return (T) newobj; // unchecked cast
    }

    public static <T> Class<? extends T> getComponentType(T[] a) {
        Class<?> k = a.getClass().getComponentType();
        return (Class<? extends T>) k; // unchecked cast
    }

    public static <T> T[] newArray(Class<? extends T> k, int size) {
        if (k.isPrimitive())
            throw new IllegalArgumentException
                    ("Argument cannot be primitive: " + k);
        Object a = java.lang.reflect.Array.newInstance(k, size);
        return (T[]) a; // unchecked cast
    }

    public static <T> T[] newArray(T[] a, int size) {
        return newArray(getComponentType(a), size);
    }
}

7.5 Reflection for Generics

although the reified type information for objects and class tokens contains no information about generic types, the actual bytecode of the class does encode information about generic types as well as erased types.

7.6 Reflecting Generic Types

反射类库提供了描述泛型类型的Type接口

  1. Class实现了Type接口,代表原生类型或者raw type
  2. ParameterizedType继承了Type接口,代表了泛型类或者接口的参数类型应用,从中可以提取参数类型数组
  3. TypeVariable继承了Type接口,代表了类型变量,从中可以提取类型变量的界限
  4. GenericArrayType继承了Type接口,代表了数组,从中可以提取元素类型
  5. WildCardType继承了Type接口,代表了通配符,从中可以提取通配符的上或者下类型界限

基本使用demo

class ReflectionDemo {
    private final static PrintStream out = System.out;

    public static void printSuperclass(Type sup) {
        if (sup != null && !sup.equals(Object.class)) {
            out.print("extends ");
            printType(sup);
            out.println();
        }
    }

    public static void printInterfaces(Type[] impls) {
        if (impls != null && impls.length > 0) {
            out.print("implements ");
            int i = 0;
            for (Type impl : impls) {
                if (i++ > 0) out.print(",");
                printType(impl);
            }
            out.println();
        }
    }

    public static void printTypeParameters(TypeVariable<?>[] vars) {
        if (vars != null && vars.length > 0) {
            out.print("<");
            int i = 0;
            for (TypeVariable<?> var : vars) {
                if (i++ > 0) out.print(",");
                out.print(var.getName());
                printBounds(var.getBounds());
            }
            out.print(">");
        }
    }

    public static void printBounds(Type[] bounds) {
        if (bounds != null && bounds.length > 0
                && !(bounds.length == 1 && bounds[0] == Object.class)) {
            out.print(" extends ");
            int i = 0;
            for (Type bound : bounds) {
                if (i++ > 0) out.print("&");
                printType(bound);
            }
        }
    }

    public static void printParams(Type[] types) {
        if (types != null && types.length > 0) {
            out.print("<");
            int i = 0;
            for (Type type : types) {
                if (i++ > 0) out.print(",");
                printType(type);
            }
            out.print(">");
        }
    }

    public static void printType(Type type) {
        if (type instanceof Class) {
            Class<?> c = (Class) type;
            out.print(c.getName());
        } else if (type instanceof ParameterizedType) {
            ParameterizedType p = (ParameterizedType) type;
            Class c = (Class) p.getRawType();
            Type o = p.getOwnerType();
            if (o != null) {
                printType(o);
                out.print(".");
            }
            out.print(c.getName());
            printParams(p.getActualTypeArguments());
        } else if (type instanceof TypeVariable<?>) {
            TypeVariable<?> v = (TypeVariable<?>) type;
            out.print(v.getName());
        } else if (type instanceof GenericArrayType) {
            GenericArrayType a = (GenericArrayType) type;
            printType(a.getGenericComponentType());
            out.print("[]");
        } else if (type instanceof WildcardType) {
            WildcardType w = (WildcardType) type;
            Type[] upper = w.getUpperBounds();
            Type[] lower = w.getLowerBounds();
            if (upper.length == 1 && lower.length == 0) {
                out.print("? extends ");
                printType(upper[0]);
            } else if (upper.length == 0 && lower.length == 1) {
                out.print("? super ");
                printType(lower[0]);
            } else throw new AssertionError();
        }
    }

    public static void printClass(Class c) {
        out.print("class ");
        out.print(c.getName());
        printTypeParameters(c.getTypeParameters());
        out.println();
        printSuperclass(c.getGenericSuperclass());
        printInterfaces(c.getGenericInterfaces());
    }

    public static void main(String[] args) throws ClassNotFoundException {
        String[] names = new String[] {"java.util.AbstractList", "java.lang.Enum"};
        for (String name : names) {
            Class<?> c = Class.forName(name);
            printClass(c);
        }
    }
}
incendi0 commented 4 years ago

Chapter 9. Design Patterns

9.1 Visitor

abstract class Tree<E> {
    abstract public String toString();
    abstract public Double sum();
    public static <E> Tree<E> leaf(final E e) {
        return new Tree<E>() {
            public String toString() {
                return e.toString();
            }
            public Double sum() {
                return ((Number)e).doubleValue();
            }
        };
    }
    public static <E> Tree<E> branch(final Tree<E> l, final Tree<E> r) {
        return new Tree<E>() {
            public String toString() {
                return "("+l.toString()+"^"+r.toString()+")";
            }
            public Double sum() {
                return l.sum() + r.sum();
            }
        };
    }
}
class TreeClient {
    public static void main(String[] args) {
        Tree<Integer> t =
                Tree.branch(Tree.branch(Tree.leaf(1),
                        Tree.leaf(2)),
                        Tree.leaf(3));
        assert t.toString().equals("((1^2)^3)");
        assert t.sum() == 6;
    }
}

上面是一个简单的tree结构和客户端,我们预先知道要实现两个api,即toString和sum,但如果要增加其他的操作,就需要修改抽象类,使用visitor模式就可以在不修改定义的情况下增加操作

abstract class Tree<E> {
    public interface Visitor<E, R> {
        R leaf(E elt);

        R branch(R left, R right);
    }

    public abstract <R> R visit(Visitor<E, R> v);

    public static <T> Tree<T> leaf(final T e) {
        return new Tree<T>() {
            public <R> R visit(Visitor<T, R> v) {
                return v.leaf(e);
            }
        };
    }

    public static <T> Tree<T> branch(final Tree<T> l, final Tree<T> r) {
        return new Tree<T>() {
            public <R> R visit(Visitor<T, R> v) {
                return v.branch(l.visit(v), r.visit(v));
            }
        };
    }
}

class TreeClient {
    public static <T> String toString(Tree<T> t) {
        return t.visit(new Tree.Visitor<T, String>() {
            public String leaf(T e) {
                return e.toString();
            }

            public String branch(String l, String r) {
                return "(" + l + "^" + r + ")";
            }
        });
    }

    public static <N extends Number> double sum(Tree<N> t) {
        return t.visit(new Tree.Visitor<N, Double>() {
            public Double leaf(N e) {
                return e.doubleValue();
            }

            public Double branch(Double l, Double r) {
                return l + r;
            }
        });
    }

    public static void main(String[] args) {
        Tree<Integer> t =
                Tree.branch(Tree.branch(Tree.leaf(1),
                        Tree.leaf(2)),
                        Tree.leaf(3));
        assert toString(t).equals("((1^2)^3)");
        assert sum(t) == 6;
    }
}

9.2 Interpreter

class Pair<A, B> {
    private final A left;
    private final B right;

    public Pair(A l, B r) {
        left = l;
        right = r;
    }

    public A left() {
        return left;
    }

    public B right() {
        return right;
    }
}

abstract class Exp<T> {
    abstract public T eval();

    static Exp<Integer> lit(final int i) {
        return new Exp<Integer>() {
            public Integer eval() {
                return i;
            }
        };
    }

    static Exp<Integer> plus(final Exp<Integer> e1, final Exp<Integer> e2) {
        return new Exp<Integer>() {
            public Integer eval() {
                return e1.eval() + e2.eval();
            }
        };
    }

    static <A, B> Exp<Pair<A, B>> pair(final Exp<A> e1, final Exp<B> e2) {
        return new Exp<Pair<A, B>>() {
            public Pair<A, B> eval() {
                return new Pair<A, B>(e1.eval(), e2.eval());
            }
        };
    }

    static <A, B> Exp<A> left(final Exp<Pair<A, B>> e) {
        return new Exp<A>() {
            public A eval() {
                return e.eval().left();
            }
        };
    }

    static <A, B> Exp<B> right(final Exp<Pair<A, B>> e) {
        return new Exp<B>() {
            public B eval() {
                return e.eval().right();
            }
        };
    }

    public static void main(String[] args) {
        Exp<Integer> e = left(pair(plus(lit(3), lit(4)), lit(5)));
        assert e.eval() == 7;
    }
}

9.4 Strategy

主要讨论parallel class hierarchies在策略模式的使用,还有recursive bound和getThis方法。

abstract class TaxPayer<P extends TaxPayer<P>> {
    public long income; // in cents
    private TaxStrategy<P> strategy;
    public TaxPayer(long income, TaxStrategy<P> strategy) {
        this.income = income; this.strategy = strategy;
    }
    protected abstract P getThis();
    public long getIncome() { return income; }
    public long computeTax() { return strategy.computeTax(getThis()); }
}
class Person extends TaxPayer<Person> {
    public Person(long income, TaxStrategy<Person> strategy) {
        super(income, strategy);
    }
    protected Person getThis() { return this; }
}
class Trust extends TaxPayer<Trust> {
    private boolean nonProfit;
    public Trust(long income, boolean nonProfit, TaxStrategy<Trust> strategy){
        super(income, strategy); this.nonProfit = nonProfit;
    }
    protected Trust getThis() { return this; }
    public boolean isNonProfit() { return nonProfit; }
}
interface TaxStrategy<P extends TaxPayer<P>> {
    public long computeTax(P p);
}
class DefaultTaxStrategy<P extends TaxPayer<P>> implements TaxStrategy<P> {
    private static final double RATE = 0.40;
    public long computeTax(P payer) {
        return Math.round(payer.getIncome() * RATE);
    }
}
class DodgingTaxStrategy<P extends TaxPayer<P>> implements TaxStrategy<P> {
    public long computeTax(P payer) { return 0; }
}
class TrustTaxStrategy extends DefaultTaxStrategy<Trust> {
    public long computeTax(Trust trust) {
        return trust.isNonProfit() ? 0 : super.computeTax(trust);
    }
}

getThis方法在类层次中的builder模式中也有用到,用于构建链式api。