"Effective Java"--Learning Notes (generics & enumerations and annotations) __java

Source: Internet
Author: User
Tags instance method int size throw exception throwable
generic type 23rd: Please do not use the original ecological type in the new code

If the original ecological type is used, it loses all the advantages of generics in terms of security and presentation, such as the use of primitive ecological types such as List, and should use types such as list<object> and list<string> 24th: Elimination of non-inspected warning

When programming with generics, you will encounter a number of compiler warnings: non-tested coercion warnings, non-client method invocation warnings, non-client common array creation warnings, and non-inspected conversion warnings. Such as:

set<lark> exaltation = new HashSet ();

=>

set<lark> exaltation = new hashset<lark> (); 

To eliminate every non-inspected warning as much as possible, if all warnings are eliminated, you can ensure that the code is type-safe, meaning that classcastexception exceptions will not occur at run time

If you cannot eliminate the warning, and you can prove that the code that caused the warning is type safe (only in this case) you can suppress the warning with a @suppresswarnings ("unchecked") annotation

Suppresswarnings annotations can be used at any level of granularity, from individual local variables to entire classes, and should always be used with suppresswarnings annotations in as small a scope as possible. Never use suppresswarnings on an entire class, and doing so may obscure important warnings

Whenever you use @suppresswarnings ("unchecked") annotations, add a comment explaining why it's safe to do so 25th: List takes precedence over arrays

An array has two important differences compared to a generic type. First, the arrays are covariant and the generics are immutable, as in the following example:

This code is legal.

Fails at runtime!
object[] Objectarray = new long[1];
Objectarray[0] = "I donot fit in";

But the code below is not legal.

Wonot compile!
list<object> O1 = new arraylist<long> ();
O1.add ("I donot fit in");

The second major difference between an array and a generic is that the array is materialized, so the arrays are known at runtime and check their element type constraints. Generics, by contrast, are implemented by Erasure, so generics only harden their type information at compile time and discard (or erase) their element type information at run time 26th: preference for generics

Using generics is more secure than using types that need to be converted in the client. Also easier, when designing new types, make sure they don't need this kind of conversion, which usually means making the class generic and, as time permits, generics the existing type. This can be easier for these types of new users without destroying the existing client 27th: prioritizing generic methods

Writing a generic method is similar to writing a generic type, declaring a type parameter list of type parameters, between the modifier of the method and its return type, in the following example, the type parameter list is <e>, and the return type is set<e>

public static <E> set<e> Union (set<e> S1, set<e> S2) {
    set<e> result = new hashset<e& gt; (s1);
    Result.addall (S2);
    return result;
}

A notable feature of a generic method is that without explicitly specifying the value of the type parameter, the compiler evaluates the value of the type parameter by examining the type of the method parameter 28th: Flexibility to use a restricted Pass Fraiti API

Java provides a special type of parameterization called a restricted wildcard type. Extends e> is called "Some subtype of E". Super E> is called "Some kind of super class of E"

For maximum flexibility, use wildcard types on input parameters that represent producers or consumers, and if parameterized types represent a T-generator, use the. Extends t>; if it represents a T consumer, use the. Super T>, can be pecs memory, Producer-extends,consumer-super, as in the following example:

list<apple> apples = new arraylist<apple> ();
list<? Extends fruit> fruits = apples;
You can only remove the data from the fruits at this time, and you cannot add the data, because the data type in fruits is a subclass of fruit, but what type of concrete is not known, so Fruits.add (...); Operation is illegal, and fruit fruit = fruits.get (0);
extends t> can only fetch (produce) data, so it is the creator (producer)

list<fruit> Fruits = new arraylist<fruit> ();
list<? Super apple> = fruits;
At this point the data type in fruits is Apple's superclass, but the specific type is not known, so the data type taken can only be object type, and Fruits.add (New Apple ()); Fruits.add (New Greenapple ( ); The operation is legal

//; Super T> can only store (consume) data, so it is the consumer (consumer)

The collections.copy () source code in

JDK 8 is as follows:

/** * Copies all of the elements from one list into another. After the * operation, the index of each copied element in the destination list * is identical to its index in the  Source list.  The destination * List must is at least as long as the source list. If It is longer, the * remaining elements in the destination list are unaffected.
 <p> * * This is runs in linear time.
 * * @param <T> The class of the objects in the lists * @param dest the destination list.
 * @param src the source list.
 * @throws indexoutofboundsexception If the destination list is too small * to contain the entire source list. * @throws unsupportedoperationexception If the destination list ' s * List-iterator does not support the <tt>
 Set</tt> operation. * * public static <T> void copy (LIST&LT; Super t> dest, list< extends t> src) {int srcsize = Src.size
    (); if (Srcsize > Dest.size ()) throw new IndexoutofboundsexcEption ("Source does not fit in dest");
        if (Srcsize < Copy_threshold | | (src instanceof randomaccess && dest instanceof randomaccess))
    {for (int i=0; i<srcsize; i++) Dest.set (i, Src.get (i));
        else {listiterator<? Super T> Di=dest.listiterator (); listiterator<?
        Extends t> si=src.listiterator ();
            for (int i=0; i<srcsize; i++) {di.next ();
        Di.set (Si.next ());
 }
    }
}

In general, if the type parameter appears only once in the method declaration, you can replace it with a wildcard and, if it is an unrestricted type parameter, replace it with an unrestricted wildcard, and if it is a restricted type parameter, replace it with a finite wildcard character 29th: Prioritize type-safe heterogeneous containers

parameterize keys (key) instead of parameterized containers, and then submits parameterized keys to the container to insert or retrieve values, using a generic system to ensure that the type of the value matches its key

The following example:

public class Favorites {
    private map<class<?>, object> Favorites = 
            new Hashmap<class<?> Object> ();

    Public <T> void Putfavorite (class<t> type, T instance) {
        if (type = = null) {
            throw new Nullpointerexcep tion ("Type is null");
        }
        Favorites.put (type, instance);

    Public <T> T getfavorite (class<t> type) {return
        type.cast (Favorites.get (type));
    }

public static void Main (string[] args) {
    Favorites f = new Favorites ();
    F.putfavorite (String.class, "Java");
    F.putfavorite (Class.class, favorites.class);
    String favoritestr = F.getfavorite (string.class);
    Class Favoriteclass = F.getfavorite (Class.class);
}

Favorites instances are heterogeneous: Unlike ordinary maps, all of its keys are of different types, so the favorites is called type-safe for heterogeneous container enumerations and annotations 30th: Substituting an int constant with an enum

An enumeration type is a type that consists of a set of fixed constants that make up a valid value, and before the programming language introduces an enumeration type, a common pattern for representing enumerated types is declaring a set of named int constants, each of which is a constant:

public static final int Apple_fuji = 0;

This approach, known as the INT enumeration pattern, has many drawbacks because the INT enumeration is a compile-time constant that is compiled into the client that uses them, and the client must recompile if the int associated with the enumeration constant changes. If there is no recompilation, the program can still run, but their behavior is indeterminate

public static final String mq_topic_vq_audio_process = "topic_vq_audio_process";

The above pattern, called the string enumeration pattern, has performance problems because it relies on string comparisons and causes the novice to Hard-code string constants into client code rather than using the appropriate domain name

Starting with Java1.5, you provide an alternative workaround that avoids the drawbacks of the int and string enumeration patterns and offers many additional benefits

public enum Apple {FUJI, prippin, granny_smith};

The Java enumeration is essentially an int value, which is the class that exports the instance to each enumerated constant through the public static final field, because there is no constructor to access, and the enumeration type is the true final

Enumeration provides compile-time type safety, and if you declare the type of a parameter to be Apple, you can guarantee that any non-null object references that are passed to the parameter must be one of three valid Apple values. An attempt to pass a value of type error causes a compile-time error

Multiple enumerated types that contain constants of the same name can coexist in a system because each type has its own namespace. You can add or rearrange constants in an enumerated type without recompiling its client code, because the field that exports the constants provides an isolation layer between the enumeration type and its clients: constant values are not compiled into client code, but in int enumeration mode

Enumeration types also allow the addition of arbitrary methods and domains, and implement arbitrary interfaces that provide advanced implementations of all object methods, implement the comparable and serializable interfaces, and design serialization for an arbitrary change of enum types

Examples are as follows:

The enum type with the data and behavior public enum Planet {MERCURY (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;         In kilograms private final double radius; In meters private final double surfacegravity;

    In m/s^2//Universal gravitational constant in m^3/kg s^2 private static final double G = 6.67300E-11;
        Constructor 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;
 F = ma}}

It is not difficult to write an enumeration type like planet, in order to associate data with an enumeration constant, declare the instance domain and write a constructor with data and save the data in the domain

If an enumeration is universally applicable, it should be a top-level class, and if it is only used in a particular top-level class, it should be a member class of that top-level class

Associating different behaviors with each enumerated constant, you can declare an abstract apply method in an enumeration type and override the abstract apply method of each constant in a specific method in a constant-specific class body, which is called a constant-specific method implementation

public enum Operation {plus ("+") {double apply (double x, double y) {return x + y;}
    }, Minus ("-") {double apply (double x, double y) {return xy;}
    The Times ("*") {double apply (double x, double y) {return x * y;}
    }, DIVIDE ("/") {double apply (double x, double y) {return x/y;}
    };
    private final String symbol;
    Operation (String symbol) {this.symbol = symbol;}

    @Override public String toString () {return symbol;}

    Abstract double apply (double x, double y);
        Implementing a fromstring to an enum type private static final map<string, operation> stringtoenum
    = new hashmap<string, operation> (); static {//Initialize map from constant name to enum constant for (Operation op:values ()) Stringtoe
    Num.put (Op.tostring (), op); }//Returns Operation for string, or null if string are invalid public static Operation fromstring (String symbOL) {return stringtoenum.get (symbol); }//Test program to perform all operations on given operands public static void Main (string[) args) {D
        ouble x = double.parsedouble (args[0]);
        Double y = double.parsedouble (args[1]); For (Operation op:Operation.values ()) System.out.printf ("%f%s%f =%f%n", X,
    OP, y, op.apply (x, y));
 }
}

An enumerated type has an automatically generated valueof (string) method that converts the name of a constant to the constant itself, and if you overwrite ToString in an enumeration type, consider writing a fromstring method that converts the custom string notation back to the corresponding enumeration

In summary, enumerations are much easier to read, more secure, and more powerful than int constants 31st: Replacing ordinal numbers with instance fields

You should never export the value associated with it based on the ordinal of an enumeration, but instead save it in an instance domain:

Enum with the integer data stored in a instance field public
enum Ensemble {
    SOLO (1), DUET (2), TRIO (3), quartet (4) , quintet (5),
    Sextet (6), Septet (7), Octet (8), Double_quartet (8),
    nonet (9), Dectet (a), Triple_quartet (a);

    private final int numberofmusicians;
    Ensemble (int size) {This.numberofmusicians = size;}
    public int Numberofmusicians () {return Numberofmusicians}}
}
32nd: Replace the bit field with Enumset

The Java.util package provides a Enumset class to effectively represent multiple sets of multiple values extracted from a single enumeration type that implements the set interface, providing rich functionality, type security, and interoperability from any other set implementation

public class Text {public
    enum Style {BOLD, italic, underline, Strikethrough}

    //Any Set could is passed in, BU  T Enumset is clearly best public
    void Applystyles (set<style> styles) {
        //body goes here
    }

    //Sample Use public
    static void Main (string[] args) {
        text text = new Text ();
        Text.applystyles (Enumset.of (Style.bold, Style.italic));
    }

33rd: Replace ordinal index with Enummap

There is a very fast map implementation that is specifically used to enumerate keys, called Java.util.EnumMap

public class herb{public enum Type {Annual, perennial, biennial};
    Private final String name;

    private final type type;
        Herb (String name, type type) {this.name = name;
    This.type = type;
    @Override public String toString () {return name;
            public static void Main (string[] args) {herb[] garden = {new Herb ("Basil", type.annual), New Herb ("Carroway", type.biennial), New Herb ("Dill", type.annual), New Herb ("Laven
        Dar ", type.perennial), New Herb (" Parsley ", type.biennial), New Herb (" Rosemary ", type.perennial)

        };
            Using a enummap to associate data with an enum Map 
34th: Using interfaces to simulate scalable enumerations 

Although it is not possible to write an extensible enumeration type, it can be emulated by writing an interface and implementing the underlying enumeration type of the interface. This allows the client to write its own enumeration to implement the interface, and if the API is written from an interface, you can use these enumerations anywhere you can use the underlying enumeration type

Emulated extensible enum using a interface public interface Operation {double apply (double x, double y);}
    Emulated extensible enum using a Interface-basic implementation public enum Basicoperation implements Operation {
    PLUS ("+") {public double apply (double x, double y) {return x + y;}
    }, Minus ("-") {public double apply (double x, double y) {return xy;}
    The 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 enum public enum 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; //Test class to exercise all operations in ' extension enum ' public static void main (string[] args) {D
        ouble x = double.parsedouble (args[0]);
        Double y = double.parsedouble (args[1]);

        Test (Extendedoperation.class, x, y);  System.out.println ();
    Print a blank line between Tests Test2 (Arrays.aslist ()), x, y); }//Test parameter is a bounded type token (Item) private static <t extends Enum<t> & Operation > Void Test (class<t> opset, double x, double y) {for (Operation op:opSet.getEnumConstants ()) System.out.printf ("%f%s%f =%f%n", x, op, y, op.apply (x, y)); }//Test parameter is a bounded wildcard type (Item) private static void Test2 (COLLECTION&LT;? extends Operat Ion> Opset, double x, double y) {for (Operation op:opset) SYSTEM.O
    ut.printf ("%f%s%f =%f%n", x, op, y, op.apply (x, y));
 }
}
35th: Annotations take precedence over naming patterns

Define an annotation type to specify simple tests that run automatically and fail when an exception is thrown

Marker annotation Type declaration
import java.lang.annotation.*;

/**
 * Indicates this annotated method is a test method.
 * use ' on parameterless static methods.
 */
@Retention (retentionpolicy.runtime)
@Target (elementtype.method) public
@interface Test {
}

The declaration of the test annotation type is itself annotated with the retention and target annotations, which are called meta annotations in the annotation type declaration. The @Retention (retentionpolicy.runtime) meta annotation indicates that the test annotation should be retained at run time, and that if there is no reservation, the testing tool will not be able to know the test annotation. The @Target (elementtype.method) meta annotation indicates that the test annotation is only valid in the method declaration: It cannot be applied to class declarations, domain declarations, or other program elements

The following is an application of the test annotation, called a markup annotation, because it has no parameters, just "callout" the annotated element, and if you misspell test, or apply the test annotation to the program element rather than the method declaration, the program cannot compile:

Program containing marker annotations public
class Sample {
    @Test public static void M1 () {}  //Test Shoul D Pass public
    static void M2 () {}
    @Test public static void M3 () {    //Test Should fail
        throw new Runtimeex Ception ("Boom");
    }
    public static void M4 () {}
    @Test the public void M5 () {}//INVALID use:nonstatic method public
    static void M6 () {
    @Test public static void M7 () {    //Test should fail
        throw new RuntimeException ("Crash");
    public static void M8 () {}
}

As in the 8 methods, only the M1 test will pass, M3 and M7 will throw an exception, M5 is an instance method, and is not part of the effective use of annotations

Annotations will never change the semantics of the annotated code, but make it possible for special processing through the tool

public class Runtests {public
    statis void Main (string[] args) throw Exception {
        int tests = 0;
        int passed = 0;
        Class TestClass = Class.forName (Args[0]);
        For (Method M:testclass.getdeclaredmethods ()) {
            if (m.isannotationpresent (Test.class)) {
                tests + +;
                try {
                    m.invoke (null);
                    Passed + +;
                } catch (InvocationTargetException wrappedexc) {
                    Throwable exc = Wrappedexc.getcause ();
                    SYSTEM.OUT.PRINTLN (M + "failed:" + exc);
                } catch (Exception exc) {
                    System.out.println ("INVALID @Test:" + M);
                }
            }
        System.out.printf ("Passed:%d, Failed:%d%n", passed, tests-passed);
    }

Runtests by calling Method.invoke to run all the methods in the class that are labeled Test, Isannotationpresent tells the tool which methods to run. If the test method throws an exception, the reflection mechanism encapsulates it in the InvocationTargetException

If you want to add support for a test that only succeeds when you throw a special exception, you need a new annotation type:

Annotation type with an array parameter

import java.lang.annotation.*;

/**
 * Indicates that this annotated method was a test method this
 * must throw the any of the designated exceptions To succeed.
 */

@Retention (retentionpolicy.runtime)
@Target (elementtype.method) public
@interface Exceptiontest {
    class< extends exception>[] value ();
}

Use this annotation

Code containing an annotation with an array parameter
@ExceptionTest ({indexoutofboundsexception.class,
            Nullpointerexception.class}) public
static void Doublybad () {
    list<string> List = new arraylist< String> ();

    The spec permits this is to throw either
    //indexoutofboundsexception or NullPointerException
    list.addal L (5, NULL);
}

Modify the test tool to handle the new exception

Array exceptiontest Processing Code
if (m.isannotationpresent (Exceptiontest.class)) {
    tests++;
    try {
        m.invoke (null);
        System.out.printf ("Test%s failed:no exception%n", m);
    } catch (Throwable wrappedexc) {
        Throwable exc = Wrappedexc.getcause ();
        class<? Extends exception>[] Exctypes =
            m.getannotation (exceptiontest.class). Value ();
        int oldpassed = passed;
        for (class<? extends exception> exctype:exctypes) {
            if (exctype.isinstance (exc)) {
                passed++;
                break;
            }
        }
        if (passed = = oldpassed)
            System.out.printf ("Test%s failed:%s%n", M, exc);
    }

All programmers should use the predefined annotation types provided by the Java platform, as well as the use of any annotations provided by the IDE or static analysis tools, which can enhance the quality of the diagnostic information provided by these tools 36th: Stick with override annotations

Stick with this annotation to prevent a large class of illegal errors

The IDE has automatic checking, which is called code validation, and if you start the appropriate code validation feature, and when there is a method that does not have override annotations but overrides the superclass method, the IDE generates a warning 37th: Define types with tag interfaces

A markup interface is an interface that does not contain a method declaration, but merely indicates (or indicates) that a class implements an interface with certain attributes, such as the Serializable interface, by implementing this interface, the class indicates that its instance can be written to ObjectOutputStream (serialized)

One advantage of marking interfaces over markup annotations is that the type of the markup interface definition is implemented by an instance of the tagged class; the markup annotation does not define such a type, which allows you to catch errors at compile time that you want to catch at run time with the markup annotation

Another advantage of marking interfaces over markup annotations is that they can be locked more precisely, and if the annotation type uses @target (elementtype.type) declarations, it can be applied to any class or interface. Suppose that a tag applies only to the implementation of a particular interface, and if it is defined as a markup interface, it can be used to extend a unique interface to its applicable interface, such as a set interface that is a restricted interface, which can describe a constraint on an entire object. Or that an instance can be processed using a method of another class

The advantage of marking annotations over markup interfaces is that it can add one or more annotation type elements by default, adding more information to the type of annotation that has been used

Another advantage of tagging annotations is that they are part of a larger annotation mechanism

If the markup is applied to any program element instead of a class or interface, the annotation must be used, and if the tag is applied only to classes and interfaces, the markup interface should be used preferentially

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.