1. Reference materials

  1. [Java8 Practical] E-book download in the attachment

2. Content depth

  1. Simple, does not involve complex and advanced usage
  2. This section describes the easiest and most effective Java8 features

3. The most useful features of Java8

Lambda expressions and method references make code more concise and reduce the number of lines of code.

2. Stream, as an advanced iterator of data set, can easily traverse, filter and count a set of data. Parallel processing is supported.

3. Interface Supports the default method and can be flexibly extended.

4. Replace null with Optional to reduce null pointer exceptions.

4. How to utilize Java8 features in AS

  1. Just configure build.gradle directly in the moudle where you want to use Java8 features
    android {
        ***
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
    Copy the code
  2. Directly search the entire project for “anonymous inner classes that can be replaced with lambda expressions” to reduce the amount of code in one click
  • Android Studio toolbar -> Analyze -> Run Inspection by Name
  • Press press with lambda. Select Anonymous type can be press with lambda
  • Select Whole Project -> OK for the scope
  • After the query of AS is complete, run Replace with lambda.
  • Here is:

Below, according to the book “Java 8 Practical” chapter order, selected part of the content in turn.

5. How has our code changed since the introduction of Java8: simply put, the amount of code has decreased since the introduction of Lambda.

Flow, method references, options, and so on are stated later.

  1. The following uses Thread creation as an example
    // The original way
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run(a) {
            Log.d(TAG, "old"); }});/ / Java8 writing
    Thread thread2 = new Thread(() -> Log.d(TAG, "java8"));
    Copy the code
  2. Take setting the View click listener as an example
    // The original way
    view.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(TAG, "old"); }});/ / Java8 writing
    view.setOnClickListener(v -> Log.d(TAG, "java8"));
    Copy the code

6. The interface supports the default method

1. What is the default method

  1. Interface in the default modifier, the interface itself has implemented the method.
  2. The default method in interface I, whose implementation class C inherits directly, does not have to be implemented by C again.
  3. The instance
    public interface I{
        void exe(a);
        // The default method, the interface itself is implemented
        default void f1(a){
            Log.d("Interface"."default Method"); }}public class C implements I{
        @Override
        public void exe(a) {}// The interface implementation class does not need to be implemented
    }
    Copy the code

2. Why provide default methods

  1. The primary purpose of the default method is to allow library designers to extend the existing interface with confidence. To avoid adding new methods to the existing interface, all implementation classes should add their implementation.
  2. In addition to facilitating interface extension, their own use when doing code reduction

    Sometimes define an interface, for the sake of extensibility will declare some methods, existing functions will not be called, or different scenarios, will call different methods;

    We will use multiple implementation class instances/anonymous inner classes of this interface for passing parameters.

    If the interface is filled with traditional abstract methods, it can lead to bloated, or redundant, code.

    By declaring the default method in the interface, you can not only retain the methods to be extended, but also more flexibly rewrite different default methods, greatly reducing the amount of code.

    public interface OriInterface {
        void f1(a);
        void f2(a);
        void f3(a);
        void f4(a);
    }
    public interface DefaultInterface {
        void f1(a);
        default void f2(a){};
        default void f3(a){};
        default void f4(a){};
    }
    public void testOriInterface(OriInterface oriInterface){}public void testOriInterfaceFunc2(OriInterface oriInterface){
        oriInterface.f2();
    }
    public void testDefaultInterface(DefaultInterface defaultInterface){}public void testDefaultInterfaceFunc2(DefaultInterface defaultInterface){
        defaultInterface.f2();
    }
    public void testInterface(a){
        // Test the raw interface
        testOriInterface(new OriInterface() {
            @Override
            public void f1(a) {}@Override
            public void f2(a) {}@Override
            public void f3(a) {}@Override
            public void f4(a) {}}); testOriInterfaceFunc2(new OriInterface() {
            @Override
            public void f1(a) {}@Override
            public void f2(a) {
                Log.d("TestInterface"."ori interface. f2.");
            }
            @Override
            public void f3(a) {}@Override
            public void f4(a) {}});// Test the interface that contains the default method
        testDefaultInterface(new DefaultInterface() {
            @Override
            public void f1(a) {}}); testDefaultInterfaceFunc2(new DefaultInterface() {
            @Override
            public void f1(a) {}@Override
            public void f2(a) {
                Log.d("TestInterface"."default interface. f2."); }}); }Copy the code

Lambda expressions

1. What is a Lambda expression

  1. Lambda expressions are a way to succinctly represent anonymous functions that can be passed.
  2. Lambda expressions have no name, but have a list of arguments, function topics, return types, and possibly a list of exceptions that can be thrown.
  3. Lambda expressions can be passed to methods as arguments.
  4. What about passing as a parameter

    The most common way to pass a block of logic/code around is to define an interface I with an instance of I in the method func argument and a method that calls the specified instance of I in the func method body, the common anonymous inner class/listener writing.

    public interface I{
        void exe(a);
    }
    public void func(I i){
        i.exe();
    }
    func(new I() {
        @Override
        public void exe(a) {
            Log.d("I"."exe"); }});Copy the code

    Lambda expressions are a simplification of the anonymous inner class/listener interface’s bloated code.

    func(()->Log.d("I"."exe"));
    Copy the code

    Lambda expressions don’t let you do things you couldn’t do before

2. Several styles of Lambda expressions

  1. General style: (Parameter list) -> {code block; return **; }
    (Parameter list) -> Single line of code (parameter list) -> {code; } (parameter list) -> {code;returnThe return value. } (parameter list) -> {returnThe return value. } (parameter list) -> Return valueCopy the code

Where Lambda expressions can be used: on functional interfaces.

  1. What is a functional interface?A functional interface is one that has only one abstract method
    • Note that the limit applies only to the number of abstract methods.
    • An interface is functional even if it contains many default /default methods and defines only one abstract method in the interface.
    • DefaultInterface has many default methods, but contains only one abstract method, which is also a functional interface.
    @FunctionalInterface
    public interface DefaultInterface {
        void f1(a);
        default void f2(a){};
        default void f3(a){};
        default void f4(a){};
    }
    Copy the code
    • For functional interfaces, the @functionalInterface annotation is recommended, but not required.
  2. Lambda expressions allow implementations of abstract methods of functional interfaces to be provided directly inline, andTake the entire Lambda expression as an instance of this functional interface.
    • Thus, passing an instance of a specified interface using an anonymous inner class is essentially the same as passing an instance using a Lambda expression.
    • Lambda expressions just make the code cleaner.
  3. The signature of an abstract method of a functional interface is essentially the same as the signature of a Lambda expression.
    • According to this rule, the corresponding Lambda expression can be written against the ‘method form’ of a functional interface
    • (type p1,type p2, ***) -> {method body; }
    @FunctionalInterface
    public interface FuncI1{
        void f(a);
    }
    @FunctionalInterface
    public interface FuncI2{
        void f1(int p1,int p2);
    }
    
    private void testLambdaFuncSign(a) {
        // Take Runnable as an example
        //Runnable is a functional interface
        //@FunctionalInterface
        //public interface Runnable {
        Runnable r1 = new Runnable() {
            @Override
            public void run(a) {}}; Runnable r2 = () -> { };// Take a custom functional interface as an example
        FuncI1 funcI11 = new FuncI1() {
            @Override
            public void f(a) {}}; FuncI1 funcI12 = () -> {}; FuncI2 funcI21 =new FuncI2() {
            @Override
            public void f1(int p1, int p2) {}}; FuncI2 funcI22 = (p1, p2) -> {}; }Copy the code

4. New functional interfaces in Java8

  1. Predicate
    • Predicate takes a generic T instance and returns a Boolean value. T->boolean.
    • Predicate is often used for filtering data in a stream, and other scenarios where a Boolean is returned based on an instance
    • The sample
    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }
    
    public class Person{
        public String name = "Li Lei";
        public String getName(a) {
            return name;
        }
        public void setName(String name) {
            this.name = name; }}@RequiresApi(api = Build.VERSION_CODES.N)
    public boolean testPerson(Person person, Predicate<Person> predicate){
        return predicate.test(person);
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testPredicate(a){
        Predicate<Person> personPredicate = person -> person.getName().equals("Han Meimei");
        testPerson(new Person(),personPredicate);
    }
    Copy the code
  2. Consumer
    • The Consumer receives an instance of generic T with no return. T->void
    • As you can see from the naming, simply consume an instance.
    • The sample
    @FunctionalInterface
    public interface Consumer<T> {
        // Simply consume 1 instance
        void accept(T t);
    }
    
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testConsumer(a){
        List<String> list = Arrays.asList("1"."2"."3");
        Consumer<String> consumer = s -> Log.d("TestConsumer"."Current item :" + s);
        list.forEach(consumer);
    }
    Copy the code
  3. Function
    • Function receives 1 instance of the generic T and returns 1 instance of the generic R. T->R
    • Can be understood as a regular input -> output.
    • The sample
    @FunctionalInterface
    public interface Function<T.R> {
        // Receive T instance, return R instance
        R apply(T t);
    }
    
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testFunction(a){
        /* Function
            
              function = new Function
             
              () { @Override public Integer apply(String s) { return Integer.parseInt(s); }}; * /
             ,>
            ,>
        // Again, use Lambda expressions to represent instances of functional interfaces
        Function<String, Integer> function = s -> Integer.parseInt(s);
        int result = function.apply("1000");
    }
    Copy the code
  4. Supplier
    • The Supplier does not accept the instance and generates a generic T instance directly. Void ->T
    • As you can tell by the name, it’s a producer
    • The sample
    @FunctionalInterface
    public interface Supplier<T> {
        T get(a);
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testSupplier(a) {
        Supplier<Person> supplier = () -> new Person();
        Person person = supplier.get();
    }
    Copy the code

5. Method references

  1. Method references let you create Lambda expressions based on existing methods.
  2. Format of method references: Target reference :: method name
  3. There are three main types of method references
    • A method reference to a static method
      • ClassName ::func
    • A method reference that points to a method specified by any class instance
      • ClassName ::func
    • A method reference to a specified method of an existing instance
      • Instance ::func
    public class PersonOpt{
        private String gainTag(Person person){
            return "Tag"+ person.getName(); }}@RequiresApi(api = Build.VERSION_CODES.N)
    public void testMethodReference(a) {
        // method references to static methods
        // This refers to a static method of the Integer class
        // Essence: classname.func (param)
        // Form: className::func
        Function<String,Integer> f1 = (s) -> Integer.parseInt(s);
        Function<String,Integer> f2 = Integer::parseInt;
    
        // a method reference that points to the specified method of any class instance
        // This refers to the non-static getName method in the Person class
        // Essence: param.func()
        // Form: className::func
        Function<Person,String> f3 = (p) -> p.getName();
        Function<Person,String> f4 = Person::getName;
    
        // a method reference to the specified method of an existing instance
        // This points to the non-static method PersonOpt of the PersonOpt instance
        // Essence: instance.func(param)
        // Instance ::func
        PersonOpt personOpt = new PersonOpt();
        Function<Person,String> f5 = new Function<Person, String>() {
            @Override
            public String apply(Person person) {
                returnpersonOpt.gainTag(person); }}; Function<Person,String> f6 = personOpt::gainTag; }Copy the code
  4. Reference to the constructor: ClassName::new
    • Constructor references are suitable for producers/suppliers
    • The constructor is of the form void->T
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testConstructorRef(a){
        Supplier<Person> supplier1 = new Supplier<Person>() {
            @Override
            public Person get(a) {
                return newPerson(); }}; Supplier<Person> supplier2 = () ->new Person();
        // Public Person(){} the constructor is void -> T
        Supplier<Person> supplier3 = Person::new;
    }
    Copy the code
  5. How do Lambda expressions and method references work

    This section uses a custom requirement as an example.

    Many of the methods we use take arguments in and return results.

    For example, enter three parameters and return one result.

    There are many methods of the same form:

    public String f1(int p1,long p2, double p3){
        return* *; }public int f2(int p1,String p2,Person p3){
        return* *; }public Person f3(int p1,long p2,Dog dog){
        return* *; }Copy the code

    Using lambda expressions and function references, ‘formal reuse’ is possible. Reduce code.

    • The corresponding functional interface is first created based on the form of the input parameter and return result. For example, you can define a functional interface that contains four generic data, with three incoming parameters that return one result.
    • Then use a Lambda expression to create a corresponding instance of the functional interface. Avoid declaring too many methods.
    //1: construct a functional interface/interface with only one abstract method
    @FunctionalInterface
    public interface CustomInterface<Source.Param1.Param2.Result>{
        Result gainResult(Source source,Param1 param1,Param2 param2);
    }
    //2: Creates an instance of the functional interface
    public void testCustomInterface(a){
        // Create the original form
        CustomInterface<String,Integer,Integer,String> c1 = new CustomInterface<String, Integer, Integer, String>() {
            @Override
            public String gainResult(String s, Integer param1, Integer param2) {
                returns.substring(param1,param2); }};// Lambda expression creation
        //(parameter list) -> {return result; }
        CustomInterface<String,Integer,Integer,String> c2 = (source,param1,param2) -> {returnsource.substring(param1,param2); };//(parameter list) -> result
        CustomInterface<String,Integer,Integer,String> c3 = (source,param1,param2) -> source.substring(param1,param2);
        // Use method references directly
        //className::func
        // The method reference here belongs to the second type: param.func()
        //param.func() variant param1.func(param2,param3)
        CustomInterface<String,Integer,Integer,String> c4 = String::substring;
    }
    Copy the code

8. The Stream to be continued

9. Optional to be continued