Last article: “Effective.Java” read the code of the grass hall notes ii

Interfaces are better than abstract classes

Java has two mechanisms for defining implementations that allow multiple types of implementations: interfaces and abstract classes. Interfaces can’t write implementations, but the rules are dead and the rules are alive. Jdk1.8 introduces the concept of default methods for interfaces that allow implementations to be written

public interface TestInterface {
	default void myPrint(a) {
		System.out.print("Tan Jingjie ----- default interface is called"); }}public class Main implements TestInterface{
	
	public static void main(String[] args) {
    // Static methods cannot call non-static methods, so instantiate. There is no implementation class for TestInterface, it calls the default method
		Main m = newMain(); m.myPrint(); }}Tan Jingjie ----- The default interface is invoked
Copy the code

So abstract classes and interfaces can provide some instance methods, their difference is the abstract class defined type must be a subclass of the abstract class, because the Java allows only a single inheritance, if an abstract class is a subclass of the abstract class, so its parent method is not implemented in the abstract class, in a nutshell is the abstract class method can not achieve, But an interface specifies that a class implements an interface method, so it must implement all methods in that interface, whether or not it uses them

As stated in the article, existing classes can be easily modified to implement a new interface. You simply add the required methods (if they don’t already exist) and add an implements clause to the class declaration. For example, when the Comparable, Iterable, and Autocloseable interfaces were added to the Java platform, many existing classes needed to implement them to improve. In general, existing classes cannot be modified to inherit a new abstract class. If you want two classes to inherit from the same abstract class, you must place it at the top of the type hierarchy, which is the ancestor of both classes. Unfortunately, this causes a lot of collateral damage to the type hierarchy, forcing all descendants of the new abstract class to subclass it, whether or not they are appropriate.

In summary, interfaces are ideal for defining mixed types, which can exist or declare other types besides the main type, but not abstract types, since a class can only have one parent class, as mentioned in the singer example

public interface Singer {
AudioClip sing(Song s);
}
public interface Songwriter {
Song compose(int chartPosition);
}
// Since some people may be singers and composers in real life, it is possible to define a third interface that inherits singer and composer
public interface SingerSongwriter extends Singer.Songwriter {
AudioClip strum(a);
void actSensitive(a);
}

Copy the code

If you have n attributes in your interface, you need 2N possible combinations. Interfaces are not the only solution, we have to choose a compromise based on the scenario.

Ps: If an interface has an explicit default implementation, the @implSpec Javadoc flag should be used. The interface is not allowed to contain instance attributes (which would result in coupling) and non-public static members (except private static methods). Default methods cannot be added to uncontrolled classes

Common Usage

An abstract skeleton implementation class is defined as AbstractInterface, such as AbstractCollection.

  • Provides all the implementation and assistance of an abstract class, but does not impose constraints on the type definition of the abstract class. For skeleton implementers, you may or may not inherit the class

Process:

  • It is essential to determine which methods are basic, which act as a skeleton to implement abstract methods in a class
  • Provide default methods for all methods that can be implemented directly on the base method

In summary, one interface is often the best way to define types that allow multiple implementations. If you export an important port, you should strongly consider providing a skeleton implementation class. Whenever possible, a skeleton implementation should be provided through a default method on the interface so that it can be used by all implementers of the interface. That is, constraints on interfaces often require skeleton implementation classes to take the form of abstract classes.

20. Design interfaces for future generations

Based on the jdK1.8 default implementation outlined above, the book states that default methods in Java can add methods to existing interfaces, but there is no guarantee that these methods will be used in all existing implementations. Default methods are injected into existing implementations without the knowledge or consent of the implementation class. Prior to Java 8, these implementations were written with default interfaces that never get any new methods. Many new default methods were added to the core collection interface of Java 8. The main reason is to make lambda expressions easier to use. The default methods for Java class libraries are high-quality generic implementations, and for the most part, they work just fine. However, it is not always possible to write a default method that preserves all invariants for each possible implementation. The example mentioned in this article is removeIf for the Collection interface. In summary, by default, the implementation class of the interface can compile without error, but will fail to run

Ps: Avoid adding new methods to the default interface, because classes that implement the default interface might implement the method automatically

21. Interfaces are used only to define types

When a class implements an interface, the interface, as a type, can be used to refer to an instance of the class. Should not be used only to export constants

Constant interface

This is wrong, you cannot implement a constant interface, a class that contains only final properties, as follows, because constants used internally by the class are implementation details, and implementing such an interface would leak constants

public interface ResultConstants {
 static final string SUCCEE_STATUS = "200";
}
Copy the code

About a counter example

// ObjectStreamConstants This constant interface is implemented by ObjectInputStream
public interface ObjectStreamConstants{}

public class ObjectInputStream
    extends InputStream implements ObjectInput.ObjectStreamConstants {}Copy the code
Solution:
  • If a constant is closely related to an existing class or interface, you should add it to that class or interface
// The Integer source code defines MIN_VALUE and MAX_VALUE
public final class Integer extends Number implements Comparable<Integer> {
    @Native public static final int   MIN_VALUE = 0x80000000;
   / /...
    }
Copy the code

Use the underscore character (_) in numeric text. As of Java 7, there is no legal underscore for numeric literal values

Impact, but can make them easier to read if used properly.

  • It can be imported staticallyimport staticTo import a number of constants

22. Class hierarchies are superior to tag classes

Java provides a form called subtyping for this class to solve the problem of the existence of tag classes

How do I convert a label class to a class hierarchy
  • Define an abstract class that contains abstract methods whose behavior does not depend on the value of the tagThe if ()Things that are in parentheses, common properties can also be in this class
  • Define concrete subclasses for each type of the original label class. Examples in this article are circles and rectangles, for example:
// The original label class
// Tagged class - vastly inferior to a class hierarchy!
class Figure {
enum Shape { RECTANGLE, CIRCLE };
// Tag field - the shape of this figure
final Shape shape;
// These fields are used only if shape is RECTANGLE
double length;
double width;
// This field is used only if shape is CIRCLE
double radius;
// Constructor for circle
Figure(double radius) {
shape = Shape.CIRCLE;
this.radius = radius;
}

// The improved structure
// Constructor for rectangle
Figure(double length, double width) {
shape = Shape.RECTANGLE;
this.length = length;
this.width = width;
}
double area(a) {
switch(shape) {
case RECTANGLE:
return length * width;
case CIRCLE:
return Math.PI * (radius * radius);
default:
throw newAssertionError(shape); }}}// Class hierarchy replacement for a tagged class
abstract class Figure {
abstract double area(a);
}
class Circle extends Figure {
final double radius;
Circle(double radius) { this.radius = radius; }
@Override double area(a) { returnMath.PI * (radius * radius); }}class Rectangle extends Figure {
final double length;
final double width;
Rectangle(double length, double width) {
this.length = length;
this.width = width;
}
@Override double area(a) { returnlength * width; }}Copy the code

This change reflects the natural hierarchy of types, improving flexibility and compile-time type checking

23. Support the use of static member classes instead of non-static classes

A nested class is a class defined in another class. Nested classes should only exist on their host class. If a nested class is useful in some other situation, then it should be a top-level class. There are four types of nested classes: static member classes, non-static member classes, nameless classes, and local classes. All but the first are called inner classes.

The purpose of public static member classes

A common use is the public help class

Static instance classes differ from non-static instance classes in the presence of static modifiers. Non-static instances are usually associated with the constructor of their host class, because an instance must be generated using a new method, called, whereas static classes can be directly named. In the form of an interview

If you declare a member class that does not need access to the host instance and make it a non-static member class, you will see the following consequences:

  • Each instance will have a hidden host instance, and memory space is required to store references, which can cause host classes to remain in memory even when the criteria for garbage collection are met, resulting in memory leaks
The purpose of private static member classes

A component that represents an object within the host

An anonymous class

There are many limitations to applicability, such as not instantiating when declared, not performing instanceof tests, not implementing multiple interfaces, etc., but anonymous classes are preferred in lambda (there is also a use for static factory methods).

A local class

Local classes are the least used of the four nested classes. A local class can be declared anywhere local variables can be declared and follows the same scoping rules. Local classes share attributes with other types of nested classes. Like member classes, they have names that can be used repeatedly. Like anonymous classes, they only contain instances if they are defined in a non-static context, and they cannot contain static members. Like anonymous classes, you should keep them short to avoid compromising readability.

If a nested class needs to be visible outside of a method, or is too long

Does not fit well with a method that uses a member class. If each instance of a member class requires a reference to its host instance, make it non-static; Otherwise, make it static. Suppose the class is inside a method. If you only need to create instances from one place and a preset type exists to characterize the class, use it as an anonymous class. Otherwise, make it a local class.

24. Limit the source file to a single top-level class

This means to avoid generating two classes within a class that users can manipulate, such as

// Error example
class Utensil {
static final String NAME = "pan";
}
class Dessert {
static final String NAME = "cake";
}

Copy the code

If you really need to define a source file into a class, static member classes are recommended for splitting

public class Test {
public static void main(String[] args) {
System.out.println(Utensil.NAME + [Dessert.NAME](http://Dessert.NAME));
}
private static class Utensil {
static final String NAME = "pan";
}
private static class Dessert {
static final String NAME = "cake"; }}Copy the code

25. Don’t use primitive types

Determine the type of an object. When using a generic interface, you must declare its type

If (o instanceof List); // If (o instanceof List)
List<String> strings = new ArrayList<>();

Copy the code

26. Eliminate non-check warnings

If you are compiling with Eclipse and the phenomenon of unchecked warnings is a yellow light bulb, then (and only) if you cannot eliminate the warnings but can prove that the code that triggered them is type-safe@ SuppressWarnings (" unchecked ")Annotations to suppress warnings, and use@SuppressWarnings Is it necessary to add a comment explaining why it is safe

The warning above is generated because the cameraManagerService is not being used

27. Lists are better than arrays

Arrays are covariant (covariant, meaning that if Sub is a subtype of Super, then the array type Sub[] is a subtype of the array type Super[].) By contrast, generics (List is a generic List, List) are immutable

Object[] objectArray = new Long[1];
objectArray[0] = "I don't fit in"; // Throws ArrayStoreException
// Won't compile!
List<Object> ol = new ArrayList<Long>(); // Incompatible types
ol.add("I don't fit in");
Copy the code

Either way, you can’t put a String in a Long container, but with an array, you’ll find an error at runtime; For lists, errors can be found at compile time. Of course we’d rather find errors at compile time.

Arrays and generics cannot be used together. For example, you cannot declare a new List[], new List[], new E[], etc

28. Give priority to generics

Sometimes we write a method without knowing the exact type of the argument and pass an Object. Although Object is an instance of all objects, passing an Object will cause the client to cast the Object that pops off the stack

public void add(Object o1){
		Integer.valueOf((String) o1);
	}
Copy the code

29. Use generic methods in preference

As classes can be generic and methods can be generic, static utility methods that operate on parameterized types are usually generic. All “algorithmic” methods in the collection, such as binarySearch and sort, are generic.

30. Use qualified wildcards to increase API flexibility

use

To represent the parent classes of various generic lists, you can use the type wildcard (?) Pass the question mark as a type argument to the List collection, writing: List

Sets the upper limit for type wildcards

Suppose we don’t want List
? You can pass various types of arguments, but you only want it to pass the parent class of one type, using List
to indicate a specific type

Sets the upper limit of type parameters

Use super to indicate upper limits on type parameters, such as List

Used to summarize

Wildcards with supertype qualification can be written to a generic object, and wildcards with subtype qualification can be read from a generic object

  • Qualified wildcards always include themselves
  • Upper bound type wildcard: The add method is limited
  • Lower bound type wildcard: The GET method is restricted
  • If you want to retrieve data from a data type, use? Extends wildcard
  • If you want to write an object to a data structure, use? Super Wildcard Wildcard type

If a parameterized type represents a T producer, use <? Extends T >; If it represents T consumer, use <? Super T >.

  • If you want to write and read, it’s best not to declare

If an input parameter is both a producer and a consumer, then the wildcard type is not good for you: you need an exact type match, which is the case without any wildcards.

  • You cannot declare both the upper and lower bounds for generic wildcards

Next article: Effective.Java