Tổng hợp các tính năng và cú pháp mới trong java 8

Java 8 là một bước tiến đỉnh cao cho ngôn ngữ Java. Những cập nhật mới của Java 8 là một việc lớn đối với Android và sẽ giúp cho các nhà phát triển dễ dàng hơn trong việc tạo ra các ứng dụng mới và dọn sạch mã hiện có.

Tóm lại, java 8 là :

“Tạo ra code sạch hơn và tinh giản toàn bộ quá trình”

Sự thay đổi từ Java 7 sang 8 đánh dấu một sự cập nhật lớn cho nền tảng mà Android và nhiều ứng dụng của nó được xây dựng.

Android O được công bố với việc sử dụng Java 8 để cải thiện hiệu năng của toàn bộ ứng dụng.

 

1- Giới thiệu

Từ khi Java ra đời và sự nâng cấp về mặt cú pháp và các tính năng, có vài dấu mốc thực sự quan trọng:
  • Java 1.0: Bắt đầu một ngôn ngữ lập trình.
  • Java 1.1, 1.2, 1.3, 1.4 về mặt cú pháp và tính năng không có nhiều thay đổi lớn.
  • Java 1.5 (Hay còn gọi là Java 5) đã có những thay đổi lớn, với việc đưa vào một vài khái niệm.
    • Generic
    • Autoboxing/Unboxing
    • Nâng cấp tính năng cho vòng lặp for (“foreach”).
    • Các kiểu liệt kê có tính an toàn (Type-safe enumerations).
    • Varargs
    • Nhập khẩu tĩnh (Static import)
    • Metadata
  • Java 6,7 không có nhiều thay đổi lớn về mặt ngôn ngữ.
  • Java 8 có một sự thay đổi lớn về mặt ngôn ngữ. Với việc đưa vào một vài khái niệm và tính năng:
    • Phương thức mặc định cho interface (Default interface methods)
    • Biểu thức Lambda (Lambda expressions)
    • Method references
    • Annotation có thể lặp (Repeatable annotations)
    • Stream

2- Phương thức mặc định cho Interface

Java 8 cho phép bạn thêm một method không trìu tượng vào interface bằng cách sử dụng từ khóa default. Các method này được hiểu nhưcác phương thức mở rộng. Đây là ví dụ đầu tiên của bạn:

public interface MyInterface {
    //Method trừu tượng
    void interfaceMethod();
    // Khai báo một method không trìu tượng.
    default void defaultMethod(){
        Log.v(TAG,"Default Method");
    }
}

Khi đó chúng ta chỉ cần implement chỉ cần thi hành method trừu tượng

 
// Class thi hành Interface MyInterface 
public class  MyInterfaceImpl implements MyInterface {

// Chỉ cần thi hành method trìu tượng của MyInterface .
@Override
public void interfaceMethod() {
    //Do someThing
}
}

3- Functional Interface (Interface tính năng)

 

Java 8 coi các Interface có duy nhất một method trừu tượng là các Functional Interface. Bạn có thể sử dụng annotation@FunctionalInterface để đánh dấu một interface của bạn là Functional Interface, điều này không bắt buộc, tuy nhiên trình biên dịch của Java sẽ thông báo lỗi cho bạn nếu vô tình thêm một method trìu tượng khác nữa vào interface có đánh dấu bởi annotation này.
Dưới đây là một số ví dụ thực hành với @FunctionalInterface:
Ví dụ dưới đây là một FunctionalInterface hợp lệ vì có duy nhất một method trừu tượng.
@FunctionalInterface
public interface Foo {
 
  void something();
 
  default void defaultMethod() {
      System.out.println("..");
  }
}

 

4- Biểu thức Lambda

Một trong những tính năng mạnh mẽ trong java 8 là Lambda Expression, nó cho phép giữ cho cú pháp rõ ràng hơn và ít thổi phồng lên.

Trước java 8 nó sẽ không có nếu không thêm tool , nhưng bây giờ Lambda Expression sẽ làm việc trên các phiên bản của Android từ 2,3 (Gingerbread) trở lên.

Tóm lại, biểu thức lambda là một thay thế của lớp bên trong vô danh khi cung cấp trình nghe sự kiện.

Ví dụ: hãy xem xét trình lắng nghe nhấp chuột cơ bản và sử dụng java8:

btnSave.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) 
     {
        Log.v(TAG, "Button clicked");
        Toast.makeText(MyActivity.this, "Main Activity",  Toast.LENGTH_LONG).show();    
     }
});
btnSave.setOnClickListener(v -> { 
  Log.v(TAG, "Button clicked");
  Toast.makeText(MyActivity.this, "Main Activity",   Toast.LENGTH_LONG).show();
});

 

5- Functional Interface API

Java 8 xây dựng sẵn một số lượng khá lớn các Functional Interface, chúng nằm trong package java.util.function, tại đây tôi sẽ hướng dẫn bạn sử dụng một số Interface đó, để hiểu hơn về biểu thức Lambda và sự tiện dụng của chúng.

5.1- java.util.function.Consumer

Consumer là một Functional interface xây dựng sẵn của Java 8, nó có một method trìu tượng duy nhất chấp nhận một tham số đầu vào, và method này không trả về gì cả.
@FunctionalInterface
public interface Consumer<T> {
 
  // Method chấp nhận một tham số đầu vào
  // và không trả về gì cả.
  void accept(T t);
 
}

Sử dụng method List.forEach(Consumer):

// java.util.List extends java.util.Collection  (extends Iterable)
 
// Interface java.util.Iterable:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
   for (T t : this) {
       action.accept(t);
   }
}

ConsumerExample.java

public class ConsumerExample {
 
  // Sử dụng method List.forEach(Consumer) theo cách Java < 8.
  // In ra danh sách các phân tử trong List.
  public static void beforeJ8() {
      List<String> list = Arrays.asList("a", "b", "c", "a1", "a2");
 
      list.forEach(new Consumer<String>() {
 
          @Override
          public void accept(String t) {
              System.out.println(t);
          }
 
      });
  }
 
  // Sử dụng method List.forEach(Consumer) theo cách Java 8.
  // Với việc sử dụng biểu thức Lambda.
  public static void java8Consumer() {
      List<String> list = Arrays.asList("a", "b", "c", "a1", "a2");
 
      list.forEach((String t) -> {
          System.out.println(t);
      });
  }
 
  // Sử dụng method List.forEach(Consumer) theo cách Java 8.
  // Với việc sử dụng biểu thức Lambda.
  // (Đơn giản hơn)
  public static void java8ConsumerMoreSimple() {
      List<String> list = Arrays.asList("a", "b", "c", "a1", "a2");
 
      list.forEach((String t) -> System.out.println(t));
  }
  
}

 

5.2- java.util.function.Predicate

Predicate là một Functional interface xây dựng sẵn của Java 8, nó có một method trừu tượng duy nhất chấp nhận một tham số đầu vào, và method trả về giá trị boolean (true/false). Method này dùng để đánh giá tham số đầu vào phù hợp với một logic gì đó hay không.
@FunctionalInterface
public interface Predicate<T> {
 
  // Đánh giá một tham số đầu vào và trả về true hoặc false.
  boolean test(T t);
 
}

Ví dụ sau đây sẽ lọc một danh sách các tự nhiên và in ra danh sách các số lẻ, sử dụng Predicate theo cách Java8, và trước Java8.

public class PredicateExample {
 
  // Sử dụng method Stream.filter(Predicate<T>) theo cách Java < 8.
  // Lọc một danh sách các số nguyên và in ra các số lẻ.
  public static void beforeJ8() {
      List<Integer> list = Arrays.asList(1, 4, 5, 1, 7, 8);
 
      // Luồng chứa các phần tử của danh sách trên.
      Stream<Integer> stream = list.stream();
 
      // Một luồng mới chỉ chứa các số lẻ
      Stream<Integer> stream2 = stream.filter(new Predicate<Integer>() {
 
          @Override
          public boolean test(Integer t) {
              return t % 2 == 1;
          }
      });
  }
 
  // Sử dụng method Stream.filter(Predicate<T>) theo cách Java < 8.
  // Lọc một danh sách các số nguyên và in ra các số lẻ.
  // Với việc sử dụng biểu thức Lambda.
  public static void java8Predicate() {
      List<Integer> list = Arrays.asList(1, 4, 5, 1, 7, 8);
 
      // Luồng chứa các phần tử của danh sách trên.
      Stream<Integer> stream = list.stream();
 
      // Một luồng mới chỉ chứa các số lẻ
      Stream<Integer> stream2 = stream.filter(t -> {
          return t % 2 == 1;
      });
 
      // Stream.forEach(Consumer<T>)
      stream2.forEach(t -> System.out.println(t));
  }
 
  // Sử dụng method Stream.filter(Predicate<T>) theo cách Java < 8.
  // Lọc một danh sách các số nguyên và in ra các số lẻ.
  // Với việc sử dụng biểu thức Lambda.
  // Đơn giản và ngắn gọn hơn nữa.
  public static void java8ConsumerMoreSimple() {
      List<Integer> list = Arrays.asList(1, 4, 5, 1, 7, 8);
 
      // Luồng chứa các phần tử của danh sách trên.
      Stream<Integer> stream = list.stream();
 
      stream.filter(t -> t % 2 == 1).forEach(t -> System.out.println(t));
  }
 
}

 

5.3- java.util.function.Function

Function là một Functional interface xây dựng sẵn của Java 8, nó có một method trừu tượng duy nhất chấp nhận một tham số đầu vào, và method trả về một đối tượng khác.
@FunctionalInterface
public interface Function<T, R> {
 
 // Method chấp nhận một tham số
 // Và trả về một giá trị.
 R apply(T t);
 
}

Ví dụ: Cho một danh sách các String, in ra các String  trong tập hợp dưới dạng chữ hoa.

public class FunctionExample {
 
  // Sử dụng method Stream.map(Function) theo cách Java < 8.
  // In ra danh sách các phân tử trong List.
  public static void beforeJ8() {
      List<String> list = Arrays.asList("a", "c", "B", "e", "g");
 
      // Luồng chứa các phần tử của danh sách.
      Stream<String> stream = list.stream();
 
      // Stream.map(Function):
      // <R> Stream<R> map(Function<? super T, ? extends R> mapper);
 
      // Trả về một Stream mới, với các phần tử được thay đổi đi.
      Stream<String> streamUpper = stream.map(new Function<String, String>() {
 
          @Override
          public String apply(String t) {
              return t == null ? null : t.toUpperCase();
          }
 
      });
 
      streamUpper.forEach(t -> System.out.println(t));
  }
 
  public static void java8Function() {
      List<String> list = Arrays.asList("a", "c", "B", "e", "g");
 
      // Luồng chứa các phần tử của danh sách.
      Stream<String> stream = list.stream();
 
      stream.map(t -> t == null ? null : t.toUpperCase()).forEach(
              t -> System.out.println(t));
  }
 
  public static void main(String[] args) {
 
      beforeJ8();
 
      java8Function();
 
  }
  
}

 

Một số Functional interface tương tự:

  • java.util.function.IntFunction<R>
  • java.util.function.DoubleFunction<R>
  • java.util.function.LongFunction<R>
@FunctionalInterface
public interface IntFunction<R> {
  
    R apply(int value);
}
 
 
@FunctionalInterface
public interface LongFunction<R> {
  
    R apply(long value);
}
 
 
@FunctionalInterface
public interface DoubleFunction<R> {
  
    R apply(double value);
}

 

5.4- java.util.function.Supplier

Supplier là một Functional interface xây dựng sẵn của Java 8, nó có một method trừu tượng duy nhất không tham số, và trả về một đối tượng.
@FunctionalInterface
public interface Supplier<T> {
 
   // Gets a result.
   T get();
 
}
public class SupplierExample {
 
  // Một method với tham số là Supplier<String>.
  public static void display(Supplier<String> supp) {
      System.out.println(supp.get());
  }
 
  // Không sử dụng Lambda.
  public static void beforeJ8() {
      display(new Supplier<String>() {
 
          @Override
          public String get() {
              return "Hello";
          }
      });
      display(new Supplier<String>() {
 
          @Override
          public String get() {
              return "World";
          }
      });
  }
 
  // Sử dụng biểu thức Lambda.
  public static void java8Supplier() {
      display(() -> {
          return "Hello";
      });
 
      display(() -> {
          return "World";
      });
  }
 
  // Sử dụng biểu thức Lambda.
  // (Ngắn gọn hơn nữa).
  public static void java8SupplierShortest() {
      display(() -> "Hello");
 
      display(() -> "World");
  }
 
  public static void main(String[] args) {
 
  }
 
}

 

Một số Functional Interface tương tự:

  • java.util.function.BooleanSupplier
  • java.util.function.IntSupplier
  • java.util.function.DoubleSupplier
  • java.util.function.LongSupplier

 

 

6- Method reference

Đó là một tính năng có liên quan đến biểu thức Lambda. Nó cho phép bạn tham chiếu tới một method hoặc một constructor mà không cần thực thi chúng. Method references và Lambda là tương tự nhau, vì cả hai yêu cầu một loại mục tiêu là một Functional Interface tương thích.
Java 8 cho phép bạn truyền một tham chiếu của một method hoặc một constructor thông qua việc sử dụng từ khóa ::
Trước khi đi vào vấn đề hãy xem một ví dụ đơn giản.
MyFunction là một Functional Interface. Nó định nghĩa ra một method có 2 tham số int a và b, và trả về giá trị int.
@FunctionalInterface
public interface MyFunction {
 
  // Method này có 2 tham số a, b và trả về giá trị int.
  // Định nghĩa một method làm gì đó với a và b
  // và trả về một số int.
  public int doSomething(int a, int b);
  
}

MyMathUtils là class có 2 method tĩnh để tính tổng và tính hiệu 2 số int.

public class MyMathUtils {
 
  // Method này có 2 tham số a, b và trả về giá trị int.
  // Method này khác tên, nhưng có cấu trúc giống method trìu tượng
  // định nghĩa trong interface MyFunction (Functional Interface).
  public static int sum(int a, int b) {
      return a + b;
  }
 
  // Method này có 2 tham số a, b và trả về giá trị int.
  // Method này khác tên, nhưng có cấu trúc giống method trìu tượng
  // định nghĩa trong interface MyFunction (Functional Interface).
  public static int minus(int a, int b) {
      return a - b;
  }
 
}
public class MethodReferenceExample {
 
 // Tham số thứ 3 của method này là MyFunction (Một Functional Interface).
 // Khi sử dụng method này:
 // Bạn có thể truyền vào tham chiếu method giống cấu trúc method trìu tượng
 // định nghĩa trong MyFunction.
 public static int action(int a, int b, MyFunction func) {
     return func.doSomething(a, b);
 }
 
 public static void main(String[] args) {
     int a = 100;
     int b = 30;
 
     // Truyền vào tham chiếu method sum của class MyMathUtils
     // ==> 130
     int c = action(a, b, MyMathUtils::sum);
 
     System.out.println("c = " + c);
 
     // Truyền vào tham chiếu method minus của class MyMathUtils.
     // == 70
     int d = action(a, b, MyMathUtils::minus);
 
     System.out.println("d = " + d);
 
     // Truyền vào tham chiếu method subtractExact của class Math.
     // ==> 70
     int e = action(a, b, Math::subtractExact);
 
     System.out.println("e = " + e);
 
     // Truyền vào tham chiếu method min của class Math.
     // ==> 30
     int f = action(a, b, Math::min);
 
     System.out.println("f = " + f);
 }
}

Thông qua ví dụ ở trên bạn có thể thấy cách thức sử dụng từ khóa :: để truyền vào tham chiếu của một method. Nếu bạn gọi một method, mà trong method đó có một tham số là Functional Interface, bạn có thể truyền vào một tham chiếu method có cấu trúc giống với cấu trúc method định nghĩa trong Functional interface.

7. Enhancing Null Safety

Bất kì một develop cũng không thể quên được “The NullPointerExceptions“, và đó là những gì mà chuyên gia java nghĩ và giới thiệu Optional.

Optional là một đối tượng container object có thể hoặc không thể chứa một giá trị khác null. Optional object được sử dụng để đại diện cho null với giá trị vắng mặt.

Để tương tác với giá trị, Optional cung cấp một tập hợp các phương thức hữu ích trừu tượng để check null.

Điều này cho phép chúng tôi có thêm mã súc tích và khai báo tập trung vào các chi tiết mà chúng tôi quan tâm.

      Student student= new Student();
      String firstName= null;
      String lastName= new String();
  
      //Optional.ofNullable - allows passed parameter to be null.
      Optional<String> strFirstName= Optional.ofNullable(firstName);
  
      //Optional.of - throws NullPointerException if passed parameter is null
      Optional<String> strLastName= Optional.of(value2);
     Log.v(TAG,Student.display(strFirstName,strLastName));

8. Stream Api

Stream là một lớp abstract mới được giới thiệu trong Java 8. Stream cho phép chúng tôi xử lý dữ liệu theo cách khai báo hoặc nói một cách quản lý khác.
Sử dụng stream, developer không phải lo lắng về sự đồng thời và quản lý luồng vì nó xử lý dữ liệu khổng lồ rất hiệu quả.
Dưới đây là ví dụ về stream api 0 hiển thị những từ bắt đầu bằng chữ ‘a’.

List<String> strList =
    Arrays.asList("a1", "a2", "b1", "c2", "c1");

myList
    .stream()
    .filter(s -> s.startsWith("a"))
    .map(String::toUpperCase)
    .sorted()
    .forEach(System.out::println);

Tuy nhiên stream api chỉ support api >24 trong android.

Điều kiện tiên quyết để sử dụng JAVA 8 trong studio android

  1. JDK 8 (Khi phát triển các ứng dụng cho Android, việc sử dụng các tính năng ngôn ngữ Java8 là tùy chọn. Bạn có thể giữ giá trị tương thích nguồn và đích của dự án được đặt thành Java 7, nhưng bạn vẫn phải biên dịch bằng JDK 8.)
  2. Trình biên dịch JACK
  3. Android studio 2.1 trở lên (Vì Jack chỉ được hỗ trợ trên Android Studio 2.1 trở lên nên nếu bạn muốn sử dụng các tính năng ngôn ngữ Java 8, bạn cần phải sử dụng Android Studio 2.1 và cao hơn để xây dựng ứng dụng của bạn.)

Configure Gradle trong android studio để sử dụng java 8

BỎ QUA BƯỚC CONFIGURA NẾU BẠN ĐANG SỬ DỤNG ANDROID STUDIO 3.0

Để bật các tính năng ngôn ngữ Java 8 và Jack cho dự án của bạn, hãy nhập các thông số sau vào tệp build.gradle cấp build.gradle bạn:

android {
  ...
  defaultConfig {
    ...
    jackOptions {
      enabled true
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

Nguồn: viblo.asia và  o7planning

 

Nguyễn Linh

Chia sẻ để cùng tiến bộ...