Java default interface methods concrete use cases

后端 未结 5 876
广开言路
广开言路 2020-12-30 09:01

Java 9 is near to come and more features will be added to Java interfaces, like private methods. default methods in interfaces were added in Java 8, essentially

5条回答
  •  抹茶落季
    2020-12-30 09:37

    First that comes to mind is the use of default methods to support some functional programming techniques:

    @FunctionalInterface
    public interface Function3 {
    
        D apply(A a, B b, C c);
    
        default Function>> curry() {
            return a -> b -> c -> this.apply(a, b, c);
        }
    
        default Function> bindFirst(A a) {
            return b -> c -> this.apply(a, b, c);
        }
    }
    

    Sample usage:

    Function3 sum = (a, b, c) -> a + b + c;
    long result = sum.apply(1L, 2L, 3L); // 6
    
    Function>> curriedSum = sum.curry();
    result = curriedSum.apply(1L).apply(2L).apply(3L); // 6
    
    Function> incr = sum.bindFirst(1L);
    result = incr.apply(7L).apply(3L); // 11
    result = incr.apply(6L).apply(7L); // 14
    

    You can have similar binding methods for the other parameters, implemented with default methods, such as bindSecond and bindThird.

    You can use default methods to decorate the parent interface (as @holi-java explains in his answer), also there a lot of examples of the adapter pattern (currying and binding are actually adapters).


    Besides functional programming, you can use default methods to support kind of, limited multiple inheritance:

    public interface Animal {
    
        String getHabitat();
    }
    
    public interface AquaticAnimal extends Animal {
    
        @Override
        default String getHabitat() {
            return "water";
        }
    }
    
    public interface LandAnimal extends Animal {
    
        @Override
        default String getHabitat() {
            return "ground";
        }
    }
    
    public class Frog implements AquaticAnimal, LandAnimal {
    
        private int ageInDays;
    
        public Frog(int ageInDays) {
            this.ageInDays = ageInDays;
        }
    
        public void liveOneDay() {
            this.ageInDays++;
        }
    
        @Override
        public String getHabitat() {
            if (this.ageInDays < 30) { // is it a tadpole?
                return AquaticAnimal.super.getHabitat();
            } // else
            return LandAnimal.super.getHabitat();
        }
    }
    

    Sample:

    Frog frog = new Frog(29);
    
    String habitatWhenYoung = frog.getHabitat(); // water
    
    frog.liveOneDay();
    String habitatWhenOld = frog.getHabitat(); // ground
    

    Maybe not the best example, but you get the idea...


    Another usage would be traits:

    public interface WithLog {
    
        default Logger logger() {
            return LoggerFactory.getLogger(this.getClass());
        }
    }
    
    public interface WithMetrics {
    
        default MetricsService metrics() {
            return MetricsServiceFactory.getMetricsService(
                Configuration.getMetricsIP(
                    Environment.getActiveEnv())); // DEV or PROD
        }
    }
    

    Now, whenever you have a class that needs to log something and report some metrics, you could use:

    public class YourClass implements WithLog, WithMetrics {
    
        public void someLongMethod() {
    
            this.logger().info("Starting long method execution...");
    
            long start = System.nanoTime();
    
            // do some very long action
    
            long end = System.nanoTime();
    
            this.logger().info("Finished long method execution");
    
            this.metrics().reportExecutionTime("Long method: ", end - start);
        }
    }
    

    Again, this is not the best possible implementation, but just sample code to see how traits can be used via default methods.

提交回复
热议问题