Java 8 Language features ใหม่บน Android

Android ได้นำความสามารถของ Java 8 บางส่วนมาใช้งาน ซึ่งทำให้เราเขียนโค้ดได้ดีและคลีนยิ่งขึ้น

ความสามารถของ Java 8 และ APIs ที่ Android รองรับ

Android ไม่ได้รองรับทุกความสามารถของ Java 8 แต่รองรับแค่บางส่วนเท่านั้น และเมื่อเราทำการพัฒนาเราต้องทำการตั้ง targetSdkVersion ไว้ที่ Android 7.0 (API Level 24) ขึ้นไป

Reflection and language-related APIs:

Utility APIs:

การที่เราจะใช้งานความสามารถของ Java8 นั้น เราจะต้องทำการ Enabled ซะก่อน ซึ่งพระเอกของงานที่จะเป็นตัวปลุกพลังให้แอพเรานั้นชื่อว่า “Jack” โดยทำการเพิ่มคำสั่งนี้ในไฟล์ build.gradle (module-level)

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

ข้อควรรู้

Instant Run ตอนนี้ยังไม่รองรับ Jack นะครับ หากเราเปิดใช้งาน Jack ความสามารถของ Instant Run จะไม่ทำงาน

ตอนนี้เรารู้วิธีเปิดใช้งานความสามารถของ Java 8 และข้อจำกัดของมันแล้วเราก็มาลองใช้งานแต่ละความสามารถที่เด่นๆของมันกันเถอะ

Jack ถูก deprecate แล้วนะครับ ใน Android studio เวอร์ชั่นใหม่ สามารถตามไปดูวิธีเรียกใช้งานจาก http://www.somkiat.cc/android-with-java8/ ได้เลยจ้า

Default and static interface methods

อย่างที่เรารู้ๆกันคือ interface นั้นจะมีแต่ ​Method Signature เท่านั้น จะไม่มี implement การทำงานอะไรภายใน

public interface IHuman {
void run();

void eat();
}

เวลาที่ Class ใดๆก็ตามมีการ implement interface นั้นๆไปใช้งานจะต้องทำการ override Method ภายใน interface ให้ครบทุกตัว

public class Human implements IHuman {
@Override
public void run() {

}

@Override
public void eat() {

}
}

หากเราจะทำการเพิ่ม Method said() ใน Interface (IHuman) Class ที่ทำการ implements IHuman นั้นจะต้องทำการ override Method ใหม่

ถ้าเรามี Class ที่ override IHuman หลายๆClass เราต้องมาไล่ update override ให้ครบทุก Class แลดูเสียเวลาเนอะ ใน Java 8 เลยได้ทำการเพิ่มความสามารถที่จะช่วยแก้ปัญหาจุดนี้

นั้นคือ Default interface methods และไม่ใช่แค่แก้ปัญหาในการต้องไล่ update override ให้ครบทุก Class แต่ยังสามารถ implement การทำงานเข้าไปใน Method ของ interface ได้โดยตรงเลยด้วย

public interface IHuman {
void run();

void eat();

default void said(){
System.out.print("Hello");
}
}

ทำการสั่ง print “Hello” ใน interface เจ๋งป่ะละ และ Class ที่ implement interface ไปใช้นั้นไม่ต้อง override Default methods ใหม่ด้วย

เรียกใช้งานได้เหมือน Method ที่ override จาก Interface เลย

Human human = new Human();
human.run();
human.eat();
human.said();

ข้อควรรู้

Default and static interface methods นั้นรองรับเฉพาะ API Level 24 ขึ้นไปเท่านั้น

Lambda expressions

Lambda expressions นั้นเป็นอีกหนึ่งความสามารถที่หลายๆภาษาเริ่มนำเข้ามาใช้ ด้วยลักษณะการใช้งานที่กระชับทำให้โค้ดดูคลีนและอ่านง่าย ทำให้ได้รับความนิยมอย่างมาก

ผมจะยกตัวอย่างการใช้งาน Lambda expressions ที่เห็นเด่นชัดใน Android นั้นก็คือ OnClick()

button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
System.out.print("Click");
}
});

จากเดิมเราจะต้องทำการ new OnClickListener ให้กับ setOnClickListener และยังต้อง override Method เข้ามาอีก

แต่หากเราใช้ความสามารถของ Lambda expressions

button.setOnClickListener(view -> System.out.print("Click"));
//หรือ
button.setOnClickListener(view -> {
System.out.print("Click");
System.out.print("Clicks");
});

โค้ดของเราจะดูคลีนดูสะอาดตาขึ้นมาทันที และ หากเราเขียนโค้ดแบบเต็ม Android Studio ยังทำการ Suggest ให้เราใช้ Lambda expressions อีกด้วย

แต่หากอยากใช้ Lambda expressions แต่ไม่พร้อมจะใช้ Jack เนื่องด้วยข้อจำกัดหลายๆอย่างนั้น เราก็มีตัวช่วยมานำเสนอ หนึ่งในนั้นก็คือ Retrolambda

วิธีการติดตั้งนั้นใช้วิธีเพิ่มคำสั่งผ่าน Gradle สามารถทำตามได้จาก Retrolambda-GitHub

Repeating Annotations

มันคือการที่เราใช้ Annotations ซ้ำๆได้หลายครั้ง โดยปกติการสั่งใช้งาน Annotations ซ้ำกัน Compiler จะแจ้ง Error Duplicate annotations

@interface Color {
String name();
}
@Color(name = "red")
@Color(name = "blue")
@Color(name = "green")//@Color annotation repeated 3 times
class Shirt {
}

แต่หากเราจะใช้ความสามารถของ Repeating Annotations นั้น จะต้องทำการใช้ @Repeatable Interface ที่เราต้องการให้สามารถใช้ได้ซ้ำๆหลายครั้ง

@Repeatable(Colors.class)
@interface Color {
String name();
}

@Retention(RetentionPolicy.RUNTIME)
@interface Colors {
Color[] value();
}

@Color(name = "red")
@Color(name = "blue")
@Color(name = "green")//@Color annotation repeated 3 times
class Shirt {
}

จากโค้ดข้างบนเราทำการ set Color name ผ่าน Annotation Colorให้กับ Shirt สามค่าด้วยกันโดยที่ Compiler ไม่แจ้ง Error

จากนั้นเราจะลองทดสอบดูว่า Shirt มี Color name ทั้งสามค่าจริงไหม

public class RepeatingAnnotations {
public static void main(String args[]) {
Color[] colorArray = Shirt.class.getAnnotationsByType(Color.class);
for (Color color : colorArray) {
System.out.println(color.name());
}
}
}

ได้ผลออกมาตรงกับที่เรา set ค่าผ่าน Repeating Annotations เป๊ะ

red
blue
green

Method References

เป็น feature ที่เกี่ยวข้องกันกับ Lambda Expression มันช่วยให้เราเรียกใช้งาน constructors หรือ methods แบบรูปย่อได้

ประเภทของ Method References และการใช้งานแต่ละประเภทสามารถดูตัวอย่างได้จากโค้ดด้านล่างเลยจ้า คลีนขึ้นเยอะ :)

  • Reference to a static method (Class::staticMethodName)
List<Integer> list = Arrays.asList(12,5,45,18,33,24,40);

// Using an anonymous class
findNumbers(list, new BiPredicate<Integer, Integer>() {
public boolean test(Integer i1, Integer i2) {
return Numbers.isMoreThanFifty(i1, i2);
}
});

// Using a lambda expression
findNumbers(list, (i1, i2) -> Numbers.isMoreThanFifty(i1, i2));

// Using a method reference
findNumbers(list, Numbers::isMoreThanFifty);
  • Reference to a constructor (ClassName::new)
List<Shipment> l = new ArrayList<Shipment>();

// Using an anonymous class
calculateOnShipments(l, new Function<Shipment, Double>() {
public Double apply(Shipment s) { // The object
return s.calculateWeight(); // The method
}
});

// Using a lambda expression
calculateOnShipments(l, s -> s.calculateWeight());

// Using a method reference
calculateOnShipments(l, Shipment::calculateWeight);
  • Reference to an instance method of an arbitrary object of a particular type (Class::instanceMethodName)
final Mechanic mechanic = new Mechanic();
Car car = new Car();

// Using an anonymous class
execute(car, new Consumer<Car>() {
public void accept(Car c) {
mechanic.fix(c);
}
});

// Using a lambda expression
execute(car, c -> mechanic.fix(c));

// Using a method reference
execute(car, mechanic::fix);
  • Reference to an instance method of a particular object
    (object::instanceMethodName)
// Using an anonymous class
Function<String, Integer> f =
new Function<String, Integer>() {
public Integer apply(String s) {
return new Integer(s);
}
};
Integer i = f.apply(100);

// Using a lambda expression
Function<String, Integer> f = s -> new Integer(s);
Integer i = f.apply(100);

// Using a method reference
Function<String, Integer> f = Integer::new;
Integer i = f.apply(100);

Type Annotations

@NonNull
List<String> strings; //A non-null list of Strings.
เมื่อใดก็ตามที่คุณพัฒนาแอพพลิเคชั่นแอนดรอยด์ ให้ Java 8 เป็นอีกหนึ่งตัวเลือกที่คุณจะต้องนำมาใช้ร่วมกับการพัฒนานะครับ

glhf , ez , :)

New tools to help you build better apps.

blog อีกช่องทางหนึ่งของผู้เขียน : http://wanttobeanandroiddev.blogspot.com

Reference