Effective Java(Item: 23 to 37)

来源:互联网 发布:淘宝被知识产权投诉 编辑:程序博客网 时间:2024/05/16 01:49

Four: Generics
Item 23: Don’t use raw types in new code
1).If you use raw types, you lose all the safety and expressiveness benefits of generics;
2).You will lose type safety if you use a raw type like list, but not if you use a parameterized type like List;
3).You can’t put any element(other than null) into a Collection

/* *This small program can be compiled but will discover *the error till runtime, long after it has happend, and *in code that is far removed from the code containing the error. *It mean that the compiler can't help you discover the error. */import java.util.*;public class RawType {    public static void main(String[] args) {        Raw raw = new Raw();        raw.get();    }}class Raw {    private final Collection stamps;    public Raw() {        stamps = new ArrayList();        //Errorous insertion of String into stamps collection        stamps.add("aa");        //Errorous insertion of Integer into stamps ollection        stamps.add(new Integer(12));    }    public void get() {        for (Iterator i = stamps.iterator(); i.hasNext();) {            String s = (String) i.next();            System.out.println(s);        }    }}/*Output:aaException in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String    at Raw.get(RawType.java:19)        at RawType.main(RawType.java:7)*///:~ 

use Generic:

import java.util.*;public class GenericCode {    public static void main(String[] args) {        GenericType genericType = new GenericType();        genericType.get();                        }}class GenericType {        private final Collection<String> stamps;        public GenericType() {            stamps = new ArrayList<String>();            stamps.add("aa");            //Errorous insertion of Integer into stamps ollection            //compiler will discover such that error in compil-time             stamps.add(new Integer(12));                              }        public void get() {            for (Iterator i = stamps.iterator(); i.hasNext();) {                String s = (String) i.next();                System.out.println(s);                   }        }}/*Output:[wayne@waynefedora23 summer]$ javac GenericCode.javaGenericCode.java:18: 错误: 无法将接口 Collection<E>中的方法 add应用到给定类型;            stamps.add(new Integer(12));                                                    ^    需要: String    找到: Integer    原因: 无法通过方法调用转换将实际参数Integer转换为String     其中, E是类型变量:    E扩展已在接口 Collection中声明的Object    1 个错误*///:~

Item 24: Eliminate unchecked warnings
1).Eliminate every unchecked warning warning that you can;
2).If you can’t eliminate a warning, and you can prove that the code that provoked the warning is typesafe, then(and only then)suppress the warning with an @SuppressWarnings(“unchecked”)annotation;
3).Always use the SuppressWarnings annotation on the smallest scope possible;
4).Every time you use an @SuppressWarnings(“unchecked”)annotation, add a comment saying why it’s safe to do it;
For example:

public class UncheckedWarning {    static final int SIZE = 100;    static Generic<Integer>[] gia;    @SuppressWarnings("unchecked")    public static void main(String[] args) {        gia = (Generic<Integer>[]) new Generic[SIZE];          System.out.println(gia.getClass().getSimpleName());        gia[0] = new Generic<Integer>();    }}class Generic<T> {}/*Output: Generic[] *///:~

Item 25: Prefer lists to arrays
Arrays differ from generic types in two important ways. First, arrays are covariant.
Arrays and generics are type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequence, arrays provide runtime type safety but not compile-time type safety and vice versa for generics. Generally speaking, arrays and generics ddon’t mix well. If you find yourself mixing them and getting compile-time errors or warnings, your first impulse should be replace the arrays with lists.
Show you the code:

import java.util.*;public class Generic_ArrayVSList {       //runtime-time type safety    //will create a warning during compile-time     static <E> E reduceByArray(List<E> list, Function<E> f, E initVal) {        E[] snapshot = (E[])list.toArray();        E result = initVal;        for(E e : snapshot)            result = f.apply(result, e);        return result;    }    //compile-time type safety    static <E> E reduceByList(List<E> list, Function<E> f, E initVal) {        List<E> snapshot;        synchronized(list) {            snapshot = new ArrayList<E>(list);        }        E result = initVal;        for(E e : snapshot)            result = f.apply(result, e);        return result;    }    interface Function<T> {        T apply(T arg1, T arg2);    }}

Item 26: Favor generic types
In summary, generic types are safer and easier to use than types that require casts in client code. When you design new types, make sure that they can be used without such casts. This will often mean making the types generic. Generify your existing types as time permits. This will make life easier for new users of these types without breaking existing clients.
Example:

import java.util.*;public class GenericType {}//Ordinary Typesclass Stack {    private Object[] elements;    private int size = 0;    private static final int DEFAULT_INITAL_CAPACITY = 16;    public Stack() {        elements = new Object[DEFAULT_INITAL_CAPACITY];    }    public void push(Object e) {        ensureCapacity();        elements[size++] = e;    }    public Object pop() {        if(size == 0)            throw new EmptyStackException();        Object result = elements[--size];        elements[size] = null;        return result;    }    public boolean isEmpty() {        return size == 0;    }    private void ensureCapacity() {        if(elements.length == size)            elements = Arrays.copyOf(elements, 2 * size + 1);    }}//Generic Typesclass GenericStack<E> {    private E[] elements;    private int size = 0;    private static final int DEFAULT_INITAL_CAPACITY = 16;    public GenericStack() {        elements = (E[])new Object[DEFAULT_INITAL_CAPACITY];    }    public void push(E e) {        ensureCapacity();        elements[size++] = e;    }    public E pop() {        if(size == 0)            throw new EmptyStackException();        E result = elements[--size];        elements[size] = null;        return result;    }    public boolean isEmpty() {        return size == 0;    }    private void ensureCapacity() {        if(elements.length == size)            elements = Arrays.copyOf(elements, 2 * size + 1);    }}

Item 27: Favor generic methods
Just as classes can benefit from generification, so can methods. Static utility methods are particularly good candidates for generification.
A related pattern is the generic singleton factory. Because generic are implemented by erasure, you can use a sinngle object for all required type parameterizations, but you need to write a static factory method to repeatedly dole out the object for each requested type parameterization.
Here is the example:

public class GenericMethod {    //Generic singleton factory pattern    private static UnaryFunction<Object> IDENTITY_FUNCTION =         new UnaryFunction<Object>(){            public Object apply(Object arg) {return arg;}        };    @SuppressWarnings("uncheccked")    public static <T> UnaryFunction<T> identityFunction() {        return (UnaryFunction<T>) IDENTITY_FUNCTION;    }     public static void main(String[] args){        String[] strings = {"jute", "hemp", "nylon"};        UnaryFunction<String> sameString = identityFunction();        for(String s : strings)            System.out.println(sameString.apply(s));        Number[] numbers = {1, 2.0, 3L};        UnaryFunction<Number> sameNumber = identityFunction();        for(Number n : numbers)            System.out.println(sameNumber.apply(n));    }}interface UnaryFunction<T> {    T apply(T arg);}/*Output:jutehempnylon12.03*///:~

Item 28: Use bounded wildcards to increase API flexibility
1).For maximum flexibility, use wildcard types on input paramaters that represent producers or consumers;
2).PESC stands for producer-extends, consumer-super;
3).Do not use wildcard types as return types;
4).If the user of a class has to think about wildcard types, there is probably something wrong with the class’s API;
5).You should always use Comparable

public class WildCard {    static void rawArgs( Holder holder, Object arg) {        Object obj = holder.get();    }    static void unboundedArg(Holder<?> holder, Object arg) {        Object obj = holder.get();    }    static <T> T exact1(Holder<T> holder) {        T t = holder.get();        return t;    }    static <T> T exact2(Holder<T> holder, T arg) {        holder.set(arg);        T t = holder.get();        return t;    }    static <T> T wildSubtype(Holder<? extends T> holder, T arg) {        T t = holder.get();        return t;    }    static <T> void wildSupertype(Holder<? super T> holder, T arg) {        holder.set(arg);        Object obj = holder.get();    }    public static void main(String[] args) {        Holder raw = new Holder<Long>();        raw = new Holder();    }}class Holder<T> {    private T value;    public Holder() {}    public Holder(T val) { value = val; }     public void set(T val) {value = val; }    public T get() {return value; }    public boolean equals(Object obj) {        return value.equals(obj);    }}///:~

Item 29: Consider typesafe heterogeneous containers
The generic type system is used to guarantee that the type of the value agrees with its key. I would you a example:

import java.util.*;public class Favorite {    //Typesafe heterogeneous container pattern - client    public static void main(String[] args) {        FavoritesImpl f = new FavoritesImpl();        f.putFavorite(String.class, "Java");        f.putFavorite(Integer.class, 0xcafebabe);        f.putFavorite(Class.class, Favorite.class);        String favoriteString = f.getFavorite(String.class);        int favoriteInteger = f.getFavorite(Integer.class);        Class<?> favoriteClass = f.getFavorite(Class.class);        System.out.printf("%s %x %s%n", favoriteString,                favoriteInteger, favoriteClass);    }}//Typesafe heterogeneous container pattern - APIinterface Favorites {    public <T> void putFavorite(Class<T> type, T instance);    public <T> T getFavorite(Class<T> type);}//Typesafe heterogeneous container pattern - implementsclass FavoritesImpl implements Favorites {    private Map<Class<?>, Object> favorites =        new HashMap<Class<?>, Object>();    public <T> void putFavorite(Class<T> type, T instance) {        if (type == null)            throw new NullPointerException("Type is null!");        favorites.put(type, type.cast(instance));    }    public <T> T getFavorite(Class<T> type) {        return type.cast(favorites.get(type));    }}/*Output:Java cafebabe class Favorite*///:~

The thing to notice is that the wildcard type is nested: it’s not the type of the Map that’s a wildcard type but the type of its key; The next thing to notice is that the value type of the favorites Map is simply Object. When a class literal is passed among methods to communicate both compile-time and runtime type information, it is called a type token just like the example showed. Interesting, the annotations API makes extensive use of bounded type tokens.

Five: Enums and Annotation
Item 30: Use enums instead of int constants
An enumerated type is a type whose legal values consist of a fixed set of constants. Java’s enum types are full-fledged classes, far more powerful than their counterparts in these other languages, where enums are essentially int values. In a words, enum types are instance-controlled. They are a generalization os gingletons, which are essentially single-element enums.
Let’s look at how enum power:
1).To asociate data with enum constants, declare instance fields and write a constructor that takes the data and stores it in the fields;
for Example:

public class WeightTable {    public static void main(String[] args) {        double earthWeight = 5.974;        double mass = earthWeight / Planet.EARTH.surfaceGravity();        for(Planet p : Planet.values())            System.out.printf("Weight on %s is %f%n",                    p, p.surfaceWeight(mass));    }}//Enum type with data and behaviorenum Planet {    WERCURY(3.302e+23, 2.439e6),    VENUS(4.869e+24, 6.052e6),    EARTH(5.975e+24, 6.378e6),    MARS(6.419e+23, 3.393e6),    JUPITER(1.899e+27, 7.149e7),    SATURN(5.685e+26, 6.027e7),    URANUS(8.683e+25, 2.556e7),    NEPTUNE(1.024e+26, 2.477e7);    private final double mass;    private final double radius;    private final double surfaceGravity;    private static final double G = 6.67300E-11;    Planet(double mass, double radius) {        this.mass = mass;        this.radius = radius;        surfaceGravity = G * mass / (radius * radius);    }    public double mass() { return mass; }    public double radius() { return radius; }    public double surfaceGravity() { return surfaceGravity; }    public double surfaceWeight(double mass) {        return mass * surfaceGravity;    }}/*Output:Weight on WERCURY is 2.257615Weight on VENUS is 5.406775Weight on EARTH is 5.974000Weight on MARS is 2.267754Weight on JUPITER is 15.112305Weight on SATURN is 6.365381Weight on URANUS is 5.405607Weight on NEPTUNE is 6.788038*///:~

The strategy enum pattern, which is less concise than the switch statement, it is safter and more flexible; for example:

//The strategy enum patternenum PayrollDay {    MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY),     WEDNESDAY(PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY),    FRIDAY(PayType.WEEKDAY),     SATURDAY(PayType.WEEKEND),  SUNNDAY(PayType.WEEKEND);    private final PayType payType;    PayrollDay(PayType payType) { this.payType = payType; }    double day(double hoursWorked, double payRate) {        return payType.pay(hoursWorked, payRate);    }    //The strategy enum type    private enum PayType {        WEEKDAY {            double overtimePay(double hours, double payRate) {                return hours <= HOURS_PER_SHIFT ? 0 :                    (hours - HOURS_PER_SHIFT) * payRate / 2;            }        },        WEEKEND {            double overtimePay(double hours, double payRate) {                return hours * payRate / 2;            }        };        private static final int HOURS_PER_SHIFT = 8;        abstract double overtimePay(double hrs, double payRate);        double pay(double hoursWorked, double payRate) {            double basePay = hoursWorked * payRate;            return basePay + overtimePay(hoursWorked, payRate);        }    }}

But you need to pay attention of that switches on enums are good for augmenting external enum types with constant-specific behavior. A minor performance disadvantage of enums over unt constants is that there is a space and time cost to load and initialize enum types.
So when should you use enums? Anytime you need a fixed set of constants. Also, consider the strategy enum pattern if mutiple enum constants share common behaviors.

Item 31: Use instance fields instead of ordinal
Many enums are naturally associated with a single int value. All enums have an ordinal method, which return the numerical position of each enum conatant in its type. Never derive a value associated with an enum from its ordinal; stroe it in instance field instead:

//Abuse of ordinal to derive an associated value - DON'T DO THISpublic enum Ensemble {    SOLO, DUET, TRIO, QUARTET, QUINTET,    SEXTET, SEPTET, OCTET, NONET, DECTET;    public int numberOfMusicians() { return ordinal() +1; }}//Store it in an instance field insteadenum Ensemble2 {    SOLO(1), DUET(2), TRIO(3), QUARTET(4), QUINTET(5),    SEXTET(6), SEPTET(7), OCTET(8), NONET(9), DECTET(10),    TRIPLE_QUARTET(12);    private final int numberOfMusicians;    Ensemble2(int size) { this.numberOfMusicians = size; }    public int numberOfMusicians() { return numberOfMusicians; }}

And remember unless you are writing such a data structure, you are best off avoiding the ordinal method entirely.

Item 32: Use EnumSet instead of bit fields
In summary, just because an enumerated type will be used in sets, there is no reason to represent it with bit fields. The EnumSet class combines the conciseness and performance of bit fields with all the many advantages of enum types described in item 30. The one real disadvantage of EnumSet is that it is not, as or release 1.6, possible to create an immutable EnumSet, but this will likely be remedied in an upcoming release. In the meantime, you can wrap an EnumSet with Collections,.unmodifiableSet, but conciseness and performance will suffer.
For example:

import java.util.*;enum AlarmPoints {    STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3,    OFFICE4, BATHROOM, UTILITY, KITCHEN}public class EnumSets {    public static void main(String[] args) {        EnumSet<AlarmPoints> points =             EnumSet.noneOf(AlarmPoints.class);        points.add(AlarmPoints.BATHROOM);        System.out.println(points);        points.addAll(EnumSet.of(AlarmPoints.STAIR1, AlarmPoints.STAIR2, AlarmPoints.KITCHEN));        System.out.println(points);        points = EnumSet.allOf(AlarmPoints.class);        points.removeAll(EnumSet.of(AlarmPoints.STAIR1, AlarmPoints.STAIR2, AlarmPoints.KITCHEN));        System.out.println(points);        points.removeAll(EnumSet.range(AlarmPoints.OFFICE1, AlarmPoints.OFFICE4));        System.out.println(points);        points = EnumSet.complementOf(points);        System.out.println(points);    } }/*Output:[BATHROOM][STAIR1, STAIR2, BATHROOM, KITCHEN][LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY][LOBBY, BATHROOM, UTILITY][STAIR1, STAIR2, OFFICE1, OFFICE2, OFFICE3, OFFICE4, KITCHEN]*///:~

Item 33: Use EnumMap instead of ordinal indexing
In summary, it is rarely appropriate to use ordinals to index arrays: use EnumMap instead. If the relationship that you are representing is multidimensional, use EnumMap<…, EnumMap<…>> . This is a special case of the general principle that application programmers should rarely, if ever, use Enum.ordinal(item 31).
just like the code show:
1).

public class EnumMap {}class Herp {    public enum Type { SNNUAL, PERENNIAL, BIENNIAL }    private final String name;    private final Type type;    Herp(String name, Type type) {        this.name = name;        this.type = type;    }    @Override public String toString() {        return name;    }}//Using ordinl() to index an array - DON'T DO THISclass OrdinalGarden {    Herb[] garden = null;    Set<Herb>[] herbsByTypes =        (Set<Herb>[]) new Set[Herb.Type.values().length];    for(int i=0; i<herbsByType.length; i++)        herbsByType[i] = new HashSet<Herb>();    for(Herb h : garden)        herbsByType[h.type.ordinal()].add(h);    for(int i=0; i<herbsByType.length; i++) {        System.out.println("%s: %s%n",                Herb.Type.values()[i], herbsByType[i]);    }}//Using an EnumMap to associate data with an ennumclass EnumMapGarden {    Map<Herb.Type, Set<Herb>> herbsByType =         new EnumMap<Herb.Type, Set<Herb>>(Herb.Type.class);    for(Herb.Type t : Herb.Type.values())        herbsByType.put(t, new HashMap<Herb>());    for(Herb h : garden)        herbsByType.get(h.type).add(h);    System.out.println(herbsByType);}…

2).

//Using ordinal() to index array of arrays - DON'T DO THIS  public enum OrdinalPhase {    SOLID, LIQUID, GAS;    public enum Transition {        MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT;        private static final Transition[][] TRANSITIONS = {            { null, MELT, SUBLIME },            { FREEZE, null, BOIL },            { DEPOSIT, CONDENSE, null }        };        public static Transition from(OrdinalPhase src, OrdinalPhase dst) {            return TRANSITIONS[src.ordinal()][dst.ordinal()];        }    }}//Using a nested EnumMap to associate data with enum pairspublic enum EnumMapPhase {    SOLID, LIQUID, GAS;    public enum Transition {        MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),        BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),        SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID);        private final EnumMapPhase src;        private final EnumMapPhase dst;        Transition(EnumMapPhase src, EnumMapPhase dst) {            this.src = src;            this.dst = dst;        }        private static final Map<EnumMapPhase, Map<EnumMapPhase, Transition>> m =            new EnumMap<EnumMapPhase, Map<EnumMapPhase, Transition>>(EnumMapPhase.class);        static {            for(EnumMapPhase p : EnumMapPhase.values())                m.put(p, new EnumMap<EnumMapPhase, Transition>(EnumMapPhase.class));            for(Transition trans : Transitionn.values)                m.get(trans.src).put(trans.dst, trans);        }        public static Transition from(EnumMapPhase src, EnumMapPhase dst) {            return m.get(src).get(dst);        }    }  }…

Item 34: Emulate extensible enums with interfaces
While you cannot write an extensible enum type, you can emulate it by writing an interface to go with a basic enum type that implements the interface. This allows clients to write their own enums that implement the interface. These enums can then be used wherever the basic enum type can be used, assuming APIs are written in terms of the interface. A monor disadvantage of the use of interfaces to emulate extensible enums is that implementations cannot be inherited from one enum type to another.

import java.math.*;import java.util.*;public class EnumsInterface {    public static void main(String[] args) {        double x = 11.11;        double y = 22.22;        test(ExtendedOperation.class, x, y);        test2(Arrays.asList(ExtendedOperation.values()), x, y);    }    private static <T extends Enum<T> & Operation> void test(            Class<T> opSet, double x, double y) {        for(Operation op : opSet.getEnumConstants())            System.out.printf("test1-->%f %s %f = %f%n",                    x, op, y, op.apply(x, y));            }    private static void test2(            Collection<? extends Operation> opSet, double x, double y) {        for(Operation op : opSet)            System.out.printf("test2-->%f %s %f = %f%n",                    x, op, y, op.apply(x, y));            }}//Emulated extensible enum using an interfaceinterface Operation {    double apply(double x, double y);}enum BasicOperation implements Operation {    PLUS("+") {        public double apply(double x, double y) { return x + y; }    },    MINUS("-") {        public double apply(double x, double y) { return x - y; }    },    TIMES("*") {        public double apply(double x, double y) { return x * y; }    },    DIVIDE("/") {        public double apply(double x, double y) { return x / y; }    };    private final String symbol;    BasicOperation(String symbol) {        this.symbol = symbol;    }    @Override public String toString() {        return symbol;    }}//Emulated extension enumenum ExtendedOperation implements Operation {    EXP("^") {        public double apply(double x, double y) { return Math.pow(x, y); }    },    REMAINDER("%") {        public double apply(double x, double y) { return x % y; }    };    private final String symbol;    ExtendedOperation(String symbol) {        this.symbol = symbol;    }    @Override public String toString() {        return symbol;    }}/*Output:test1-->11.110000 ^ 22.220000 = 172094261773063500000000.000000test1-->11.110000 % 22.220000 = 11.110000test2-->11.110000 ^ 22.220000 = 172094261773063500000000.000000test2-->11.110000 % 22.220000 = 11.110000*///:~

Item 35: Prefer annotations to naming patterns
Prior to release 1.5, it was common to use naming patterns to indicate that some program element demanded special treatment by a tool or framework. But naming pattern has several big disadvantages:
1).Typographical errors may result in silent failures;
2).There is no way to ensure that they are used only on appropriate program elements;
3).Naming patterns provide no good way to associate parameter values with program elements;
Annotation solve all of these problems nicely. So there is simply no reason to use naming patterns now that we have annotation. But you need to pay attention is that all programmers should, however, use the predefined annotation types provided by the Java platform. I would show a simple example about annotation and you can learn more at annotation chapter in “thinking in java” if you interesting in it.

import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface TestAnnotation {}///:~

Item 36: Consistently use the Override annotations
You can figure out that item in example. It’s simple, however, the compiler can protect you from a great errors if you use the Override annotation on every method declaration that you believe to override a supertype declaration, with one exception. In concrete classes, you need not annotation methods that you believe to override abstract method declarations(though it is not harmful to do so).
Example:

import java.util.*;public class Bigram {    public static void main(String[] args) {        Set<Bigram1> s = new HashSet<Bigram1>();        for(int i = 0; i<10; i++)            for(char ch = 'a'; ch <= 'z'; ch++)                s.add(new Bigram1(ch, ch));        System.out.println("Bigram1-->"+s.size());        Set<Bigram2> s2 = new HashSet<Bigram2>();        for(int i = 0; i<10; i++)            for(char ch = 'a'; ch <= 'z'; ch++)                s2.add(new Bigram2(ch, ch));        System.out.println("Bigram2-->"+s2.size());    }}class Bigram1 {    private final char first;    private final char second;    public Bigram1 (char first, char second) {        this.first = first;        this.second = second;    }    public boolean equals(Bigram1 b) {        return b.first == first && b.second == second;    }    public int hashCode() {        return 31 * first + second;    }}class Bigram2 {    private final char first;    private final char second;    public Bigram2 (char first, char second) {        this.first = first;        this.second = second;    }    @Override    public boolean equals(Object o) {        if(!(o instanceof Bigram2))            return false;        Bigram2 b = (Bigram2) o;        return b.first == first && b.second == second;    }    @Override    public int hashCode() {        return 31 * first + second;    }}/*OutputBigram1-->260Bigram2-->26*///:~

Item 37: Use marker interface to define types
A marker interface is an interface that contains no method declarations, but merely designates(or “marks”) a class that implements the interface as having some property.
Marker interface VS marker annotation:
+1).Marker interfaces define a type a type that is implemented by instances of the marked class; marker annotation do not;
+2).Another advantage of marker interfaces over marker annotations is that they can be targeted more precisely;
-1).The chief advantage of marker annotation over marker interfaces is that it is possible to add more information to an annotation type after it is already in use, by adding one or more annotation type elements with default;
-2).Clearly you must use an annotation if the marker applies to any program element other than a class or extend an interface, as only classes and interfaces can be made to implement or extends an interface;
In summary, marker interface and marker annotation both have their uses. If you want to define a type that does not have any new methods associated with it, a marker interface is the way to go. If you want to mark program element other than classes and interfaces, to allow for the possibility of adding more information to the marker in the future, or to fit the marker into a framework that already makes heavy use of annotation types, then a marker annotation is the correct choice. If you find yourself writing a marker annotation type whose target is ElementType.TYPE, take the time to figure out whether it really should be an annotation type, or whether a marker interface would be more appropriate.

//Marker interfacepublic interface Set<E> extends Collection<E> {}
0 0