Java8 Features Lecture 4: Java 8 interface default method implementation

Java8 Features Lecture 4: Java8 interface default method implementation

This article is the fourth lecture of Java8 features, mainly explaining the implementation of Java 8 interface default methods . To understand the default method of Java 8, you need to understand several questions: 1. Why is there a default method? 2. If there is a default method in an interface, and the class can implement multiple interfaces, what is the difference between that and an abstract class? 3. What should I do if the default method of multiple implementations conflicts?

1. What is the default method and why should there be a default method?

1.1. Case

An interface A, the Clazz class implements interface A.

public interface A {
    
    
    default void foo(){
    
    
       System.out.println("Calling A.foo()");
    }
}

public class Clazz implements A {
    
    
    public static void main(String[] args){
    
    
       Clazz clazz = new Clazz();
       clazz.foo();//调用A.foo()
    }
}

The code compiles even though the Clazz class doesn't implement the foo() method . A default implementation of the foo() method is provided in interface A.

1.2, what is the default method

Simply put, an interface can have implementation methods, and no implementation class is required to implement its methods. Just add a default keyword in front of the method name.

1.3, why the default method appears

Why have this feature? First of all, the previous interface is a double-edged sword. The advantage is that it is oriented to abstraction rather than specific programming. The disadvantage is that when the interface needs to be modified, all classes that implement the interface need to be modified . However, for released versions, there is no way to add new methods to the interface without affecting the existing implementation. So introduce the default method. Their purpose is to solve the problem that the modification of the interface is not compatible with the existing implementation .

Background : Java8 provides new methods for many APIs. For example, the commonly used java.util.List interface adds three methods: replaceAll, sort, and spliterator; according to the current Java syntax specification, all subclasses of List need to implement these three methods. This is obviously unacceptable, especially since many third-party libraries depend on JDK, and it is impossible to require them to modify them together .

  • In order to solve this problem and ensure JDK compatibility, Java8 introduces a new mechanism: interfaces can declare methods with implementations .

grammar rules

  • 1. Use the default keyword
  • 2. An interface can contain multiple methods with default implementations

The source code of the three new methods in java.util.List

public interface List<E> extends Collection<E> {
    
    
    default void replaceAll(UnaryOperator<E> operator) {
    
    
        Objects.requireNonNull(operator);
        final ListIterator<E> li = this.listIterator();
        while (li.hasNext()) {
    
    
            li.set(operator.apply(li.next()));
        }
    }
    
    default void sort(Comparator<? super E> c) {
    
    
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
    
    
            i.next();
            i.set((E) e);
        }
    }
    
    default Spliterator<E> spliterator() {
    
    
        return Spliterators.spliterator(this, Spliterator.ORDERED);
    }
}

2. Java 8 abstract class and interface comparison

After this feature came out, some people reported that the interfaces of java 8 have implementation methods, isn't it similar to abstract classes ? In fact, there are still some, please see the comparison table below. .

Same point difference
are abstract types Abstract classes cannot have multiple inheritance, interfaces can (whether it is multiple type inheritance or multiple behavior inheritance)
Both can have implementation methods (interfaces did not work before) Abstract classes and interfaces reflect different design concepts. In fact, the abstract class represents the "is-a" relationship, and the interface represents the "like-a" relationship
You don’t need to implement classes or inheritors to implement all methods, (it didn’t work before, now the default method in the interface does not need to be implemented by the implementer) The variables defined in the interface are of public static final type by default, and their initial values ​​must be given, so their values ​​cannot be changed in the implementation class; the variables in the abstract class are of friendly type by default, and their values ​​can be redefined in subclasses or reassigned.

3. Multiple inheritance conflicts

Since the same method can be introduced from different interfaces, there will naturally be conflicts. The same class may inherit methods with the same signature from different interfaces . How to solve this?

The rules for judging conflicts in the default method are as follows:

1. A method declared in a class takes precedence over any default method (classes always win)

2. Otherwise, the shortest path will be selected first .

3.1. Examples

scene 1:

public interface A {
    
    
    default String test() {
    
    
        return "A";
    }
}
public class B {
    
    
    public String test() {
    
    
        return "B";
    }
}
public class C extends B implements A {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(new C().test());
    }
}
  • In this example, both A and B in C respectively inherit the test() method, but B is a class, and A is an interface, so the test() method in B has a higher priority, so the output result this time should be: B

Scene two:

public interface D extends A {
    
    
    default String test() {
    
    
        return "D";
    }
}
public class E implements A, D {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(new E().test());
    }
}
  • In this example, the two interfaces A and D in E respectively inherit the test() method, but D is a subinterface of A, so the test() method in the D interface has a higher priority, so the output here should be: D

Scene three:

public class F implements A {
    
    
    
}
public class G extends F implements A, D {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(new G().test());
    }
}
  • In this scenario, although F implements the A interface, because it does not rewrite the test() method, the priority is still higher than the D interface, so the output here is still: D

Scene four:

public interface H {
    
    
    default String test() {
    
    
        return "H";
    }
}
public class K implements A, H {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(new K().test());
    }
    
    @Override
    public String test() {
    
    
        return H.super.test();
    }
}
  • In this scenario, since A and H have no inheritance relationship, the JVM cannot recognize which method in the interface to call, so it is necessary to explicitly override the test() method in class K to specify which method in the interface to call, of course, you can also rewrite the method yourself ;

4. The use of the default method in the project

Scenario 1 : In the rule engine implementation class, we use the default method to identify the execution stage, so that subclasses do not need to rewrite this method, which can save code.

/**
 * 业务检查
 **/
public interface BizChecker extends Checker {
    
    
    /**
     * 适用什么阶段
     *
     * @return
     */
    @Override
    @NotNull
    default PhaseEnum phase() {
    
    
        return PhaseEnum.BIZ_CHECK;
    }
}

Scenario 2 : In the error code interface, set the method of getting the error string as the default method, which can save the amount of code.

/**
 * 错误码接口
 **/
public interface ErrorCode {
    
    

    /**
     * 错误码 必须在{@link ErrorCodeEnum} 中定义
     * @return
     * @see {@link ErrorCodeEnum#getErrCode()}
     */
    String getErrCode();

    /**
     * 错误描述
     * @return
     */
    String getErrDesc();

    /**
     * 是否用在response里对外提示
     * @return
     */
    boolean isFirstTip();

    /**
     * 获取错误字符串:code+desc
     * @return
     */
    default String getErrString() {
    
    
        return "code:" + getErrCode() + ",desc:" + getErrDesc() + ",useInResp:" + isFirstTip();
    }
}

Scenario 3 : Current limiter default method

/**
 * @Description 限流器
 */
public interface RateLimiter {
    
    
    default void acquire() {
    
    
        acquire(1);
    }

    /**
     * 限流
     * @param permits 请求资源数
     */
    void acquire(int permits);
}

Scenario 4 : Handle commodity price limits, and the default method is placed on the interface

public interface GoodsFirmHighestPricePretreated {
    
    

  	/**
     * 处理最高限价针对商品简化
     */
    default Response<Boolean> handleFirmHighestPrice(FullItemDTO fullItemDTO, GoodsPriceContext context) {
    
    
        if (CollectionUtils.isEmpty(fullItemDTO.getBaseItem().getSkus())) {
    
    
            return Response.ok(true);
        }
    }
}

5. Summary

The default method provides convenience for us to modify the interface without destroying the structure of the original implementation class. At present, the collection framework of Java 8 has used the default method to improve it. When we finally start using the lambdas expression of Java 8, it provides us with a smooth transition experience. Maybe in the future we will see more default methods used in API design.

Guess you like

Origin blog.csdn.net/qq_28959087/article/details/131710565
Recommended