Apa antarmuka fungsional yang digunakan untuk Java 8?

154

Saya menemukan istilah baru di Java 8: "antarmuka fungsional". Saya hanya bisa menemukan satu penggunaannya ketika bekerja dengan ekspresi lambda .

Java 8 menyediakan beberapa antarmuka fungsional bawaan dan jika kita ingin mendefinisikan antarmuka fungsional apa pun maka kita dapat memanfaatkan @FunctionalInterfaceanotasi tersebut. Ini akan memungkinkan kita untuk mendeklarasikan hanya satu metode di antarmuka.

Sebagai contoh:

@FunctionalInterface
interface MathOperation {
    int operation(int a, int b);
}

Seberapa berguna itu di Java 8 selain hanya bekerja dengan ekspresi lambda ?

(Pertanyaan di sini berbeda dari yang saya tanyakan. Pertanyaannya adalah mengapa kita membutuhkan antarmuka fungsional saat bekerja dengan ekspresi lambda. Pertanyaan saya adalah: mengapa penggunaan lain yang dilakukan antarmuka fungsional selain dengan ekspresi lambda?)

Madhusudan
sumber
1
Tampaknya duplcate ke tautan ini. Mereka juga berbicara tentang mengapa hanya ada satu metode di Antarmuka Fungsional. stackoverflow.com/questions/33010594/…
Kulbhushan Singh
1
@KulbhushanSingh Saya melihat pertanyaan ini sebelum memposting ... Kedua pertanyaan itu terasa berbeda ...
Madhusudan

Jawaban:

127

@FunctionalInterfaceanotasi berguna untuk memeriksa waktu kompilasi kode Anda. Anda tidak dapat memiliki lebih dari satu metode selain itu static, defaultdan metode abstrak yang menimpa metode di Objectdalam Anda @FunctionalInterfaceatau antarmuka lain yang digunakan sebagai antarmuka fungsional.

Tetapi Anda dapat menggunakan lambdas tanpa anotasi ini dan Anda juga dapat mengganti metode tanpa @Overrideanotasi.

Dari dokumen

antarmuka fungsional memiliki tepat satu metode abstrak. Karena metode default memiliki implementasi, mereka tidak abstrak. Jika sebuah antarmuka mendeklarasikan metode abstrak yang menimpa salah satu metode publik java.lang.Object, itu juga tidak diperhitungkan dalam penghitungan metode abstrak antarmuka karena setiap implementasi antarmuka akan memiliki implementasi dari java.lang.Object atau di tempat lain.

Ini dapat digunakan dalam ekspresi lambda:

public interface Foo {
  public void doSomething();
}

Ini tidak dapat digunakan dalam ekspresi lambda:

public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

Tetapi ini akan memberikan kesalahan kompilasi :

@FunctionalInterface
public interface Foo {
  public void doSomething();
  public void doSomethingElse();
}

Anotasi '@FunctionalInterface' tidak valid; Foo bukan antarmuka fungsional

Sergii Bishyr
sumber
43
Untuk lebih tepatnya, Anda harus memiliki tepat satu metode abstrak yang tidak menimpa metode di java.lang.Objectdalam antarmuka fungsional.
Holger
9
... dan itu sedikit berbeda dengan "tidak memiliki lebih dari satu publicmetode selain staticdan default" ...
Holger
4
Masih tidak mengerti gunanya memilikinya. Mengapa ada orang di bumi yang mengganggu memeriksa berapa banyak metode yang dimiliki antarmuka-nya. Antarmuka penanda masih memiliki titik dan tujuan tertentu. Dokumentasi dan jawabannya hanya menjelaskan apa yang dilakukannya, bukan bagaimana penggunaannya sama sekali. Dan "gunakan" persis seperti yang diminta OP. Jadi saya tidak akan merekomendasikan jawaban ini.
saran3h
1
@ VNT kesalahan kompilasi mendapatkan klien dari antarmuka ini, tetapi tidak antarmuka itu sendiri dapat berubah. Dengan anotasi ini, kesalahan kompilasi ada di antarmuka, sehingga Anda memastikan bahwa tidak ada yang akan merusak klien dari antarmuka Anda.
Sergii Bishyr
2
Ini menunjukkan bagaimana cara menggunakannya tetapi tidak menjelaskan mengapa kita membutuhkannya.
sheikh
14

The dokumentasi membuat memang perbedaan antara tujuan

Tipe anotasi informatif yang digunakan untuk menunjukkan bahwa deklarasi tipe antarmuka dimaksudkan sebagai antarmuka fungsional seperti yang didefinisikan oleh Spesifikasi Bahasa Java.

dan use case

Perhatikan bahwa instance antarmuka fungsional dapat dibuat dengan ekspresi lambda, referensi metode, atau referensi konstruktor.

yang kata-katanya tidak menghalangi kasus penggunaan lain secara umum. Karena tujuan utamanya adalah untuk menunjukkan antarmuka fungsional , pertanyaan Anda yang sebenarnya bermuara pada "Apakah ada kasus penggunaan lain untuk antarmuka fungsional selain ekspresi lambda dan referensi metode / konstruktor?"

Karena antarmuka fungsional adalah konstruksi bahasa Jawa yang ditentukan oleh Spesifikasi Bahasa Jawa, hanya spesifikasi itu yang dapat menjawab pertanyaan itu:

JLS §9.8. Antarmuka Fungsional :

...

Selain proses biasa membuat instance antarmuka dengan mendeklarasikan dan membuat instance kelas (§15.9), instance antarmuka fungsional dapat dibuat dengan ekspresi referensi metode dan ekspresi lambda (§15.13, §15.27).

Jadi Spesifikasi Bahasa Jawa tidak mengatakan sebaliknya, satu-satunya use case yang disebutkan dalam bagian itu adalah membuat instance antarmuka dengan ekspresi referensi metode dan ekspresi lambda. (Ini termasuk referensi konstruktor karena mereka dicatat sebagai salah satu bentuk ekspresi referensi metode dalam spesifikasi).

Jadi dalam satu kalimat, tidak, tidak ada use case lain untuk itu di Java 8.

Holger
sumber
Mungkin hanya akan meminta sedikit terlalu banyak atau tidak relevan (Anda dapat memilih untuk tidak menjawab), tetapi apa yang akan Anda sarankan ketika seseorang membuat utilitas public static String generateTaskId()versus membuatnya lebih "fungsional" orang lain memilih untuk menulisnya seperti public class TaskIdSupplier implements Supplier<String>dengan getmetode menggunakan implementasi generasi yang ada. Apakah itu penyalahgunaan antarmuka fungsional, terutama menggunakan kembali Supplierdari JDK built-in? PS: Saya tidak bisa menemukan tempat yang lebih baik / tanya jawab untuk menanyakan hal ini. Senang bermigrasi jika Anda bisa menyarankan.
Naman
1
@ Naman Anda tidak membuat metode utilitas lebih fungsional ketika Anda membuat kelas bernama TaskIdSupplier. Sekarang, pertanyaannya adalah mengapa Anda membuat kelas bernama. Ada beberapa skenario di mana jenis nama seperti itu diperlukan, misalnya ketika Anda ingin mendukung menemukan implementasi melalui ServiceLoader. Tidak ada yang salah dengan membiarkannya menerapkannya Supplier. Tetapi ketika Anda tidak membutuhkannya, jangan membuatnya. Ketika Anda hanya membutuhkan Supplier<String>, itu sudah cukup untuk digunakan DeclaringClass::generateTaskIddan menghilangkan kebutuhan untuk kelas eksplisit adalah titik fitur bahasa ini.
Holger
Sejujurnya, saya mencari pembenaran untuk rekomendasi yang saya sampaikan. Untuk beberapa alasan di tempat kerja saya tidak benar-benar merasa bahwa TaskIdSupplierpenerapannya sepadan dengan usaha, tetapi kemudian konsep ServiceLoaderbenar - benar terlintas di benak saya. Mengalami beberapa pertanyaan selama diskusi ini kami sedang seperti Apa penggunaan Supplier's publickeberadaan ketika salah satu dapat melanjutkan dan mengembangkan antarmuka mereka sendiri? dan Mengapa tidak memiliki public static Supplier<String> TASK_ID_SUPPLIER = () ->...konstanta global? . (1/2)
Naman
1
@Naman cara idiomatis untuk merepresentasikan fungsi di Java adalah metode dan mengevaluasi fungsi-fungsi itu identik dengan menjalankannya. Seharusnya tidak pernah pengembang dipaksa untuk melakukan variable.genericMethodName(args)bukan meaningfulMethodName(args). Menggunakan tipe kelas untuk mewakili suatu fungsi, baik melalui ekspresi lambda / metode referensi atau kelas yang dibuat secara manual, hanya kendaraan untuk melewati fungsi sekitar (dengan tidak adanya tipe fungsi sebenarnya di Jawa). Ini hanya boleh dilakukan bila diperlukan.
Holger
1
Saat Anda memiliki fragmen kode kecil yang hanya diedarkan, Anda dapat membuat ekspresi lambda yang mengenkapsulasinya. Setiap kali ada kebutuhan untuk memanggilnya seperti metode (ini termasuk skenario dengan kebutuhan untuk pengujian, ketika fragmen kode tidak sepele), buat metode bernama yang dapat dipanggil dan menggunakan referensi metode atau ekspresi lambda / kelas eksplisit merangkum panggilan, untuk menyebarkannya saat dibutuhkan. Konstanta hanya berguna ketika Anda tidak mempercayai efisiensi ekspresi lambda atau referensi metode yang tertanam dalam kode Anda, dengan kata lain, mereka hampir tidak pernah dibutuhkan.
Holger
12

Seperti yang orang lain katakan, antarmuka fungsional adalah antarmuka yang memaparkan satu metode. Mungkin memiliki lebih dari satu metode, tetapi semua yang lain harus memiliki implementasi standar. Alasan itu disebut "antarmuka fungsional" adalah karena ia secara efektif bertindak sebagai fungsi. Karena Anda dapat melewatkan antarmuka sebagai parameter, itu berarti bahwa fungsi sekarang "warga negara kelas satu" seperti dalam bahasa pemrograman fungsional. Ini memiliki banyak manfaat, dan Anda akan melihatnya cukup banyak ketika menggunakan Stream API. Tentu saja, ekspresi lambda adalah penggunaan utama yang jelas bagi mereka.

Sina Madani
sumber
10

Tidak semuanya. Ekspresi Lambda adalah satu-satunya poin dari penjelasan itu.

Louis Wasserman
sumber
6
Yah, lamdbas bekerja tanpa anotasi juga. Ini adalah pernyataan yang ingin @Overridememberi tahu kompiler bahwa Anda bermaksud menulis sesuatu yang "fungsional" (dan mendapatkan kesalahan jika Anda menyelinap).
Thilo
1
Langsung ke intinya dan jawaban yang benar, meski agak pendek. Saya meluangkan waktu untuk menambahkan jawaban yang lebih rumit mengatakan hal yang sama dengan lebih banyak kata ...
Holger
5

Ekspresi lambda dapat ditugaskan ke jenis antarmuka fungsional, tetapi begitu juga metode referensi, dan kelas anonim.

Satu hal yang menyenangkan tentang antarmuka fungsional tertentu dalam java.util.functionadalah bahwa mereka dapat disusun untuk membuat fungsi baru (seperti Function.andThendan Function.compose, Predicate.and, dll) karena metode standar berguna yang dikandungnya.

Hank D
sumber
Anda harus menguraikan komentar ini lebih lanjut. Bagaimana dengan referensi metode dan fungsi baru?
K.Nicholas
5

Antarmuka dengan hanya satu metode abstrak disebut Antarmuka Fungsional. Ini tidak wajib untuk menggunakan @FunctionalInterface, tetapi praktik terbaik untuk menggunakannya dengan antarmuka fungsional untuk menghindari penambahan metode tambahan secara tidak sengaja. Jika antarmuka dianotasi dengan anotasi @FunctionalInterface dan kami mencoba untuk memiliki lebih dari satu metode abstrak, ia melempar kesalahan kompiler.

package com.akhi;
    @FunctionalInterface
    public interface FucnctionalDemo {

      void letsDoSomething();
      //void letsGo();      //invalid because another abstract method does not allow
      public String toString();    // valid because toString from Object 
      public boolean equals(Object o); //valid

      public static int sum(int a,int b)   // valid because method static
        {   
            return a+b;
        }
        public default int sub(int a,int b)   //valid because method default
        {
            return a-b;
        }
    }
Akhilesh
sumber
3

Antarmuka fungsional:

  • Diperkenalkan di Jawa 8
  • Antarmuka yang berisi metode "abstrak tunggal".

Contoh 1:

   interface CalcArea {   // --functional interface
        double calcArea(double rad);
    }           

Contoh 2:

interface CalcGeometry { // --functional interface
    double calcArea(double rad);
    default double calcPeri(double rad) {
        return 0.0;
    }
}       

Contoh 3:

interface CalcGeometry {  // -- not functional interface
    double calcArea(double rad);
    double calcPeri(double rad);
}   

Anotasi Java8 - @FunctionalInterface

  • Anotasi memeriksa bahwa antarmuka hanya berisi satu metode abstrak. Jika tidak, tingkatkan kesalahan.
  • Meskipun @FunctionalInterface hilang, itu masih antarmuka fungsional (jika memiliki metode abstrak tunggal). Anotasi membantu menghindari kesalahan.
  • Antarmuka fungsional mungkin memiliki metode statis & default tambahan.
  • mis. Iterable <>, Sebanding <>, Pembanding <>.

Aplikasi Antarmuka Fungsional:

  • Referensi metode
  • Ekspresi Lambda
  • Referensi konstruktor

Untuk mempelajari antarmuka fungsional, mempelajari metode default pertama di antarmuka, dan setelah mempelajari antarmuka fungsional, akan mudah bagi Anda untuk memahami referensi metode dan ekspresi lambda

Ketan
sumber
Haruskah dua contoh pertama Anda memiliki kata kunci 'abstrak'?
sofs1
1
@ sofs1 Metode yang dideklarasikan dalam antarmuka secara default bersifat publik dan abstrak. Anda harus menggunakan kata kunci abstrak jika ada metode di kelas abstrak. Namun tidak apa-apa menggunakan kata kunci abstrak untuk metode dalam antarmuka juga. Mereka mengizinkannya untuk kompatibilitas versi java yang lebih lama tetapi tidak disarankan.
Ketan
2

Anda dapat menggunakan lambda di Java 8

public static void main(String[] args) {
    tentimes(inputPrm - > System.out.println(inputPrm));
    //tentimes(System.out::println);  // You can also replace lambda with static method reference
}

public static void tentimes(Consumer myFunction) {
    for (int i = 0; i < 10; i++)
        myFunction.accept("hello");
}

Untuk info lebih lanjut tentang Java Lambdas dan FunctionalInterfaces

Websterix
sumber
1

@FunctionalInterface adalah anotasi baru yang dirilis dengan Java 8 dan memberikan tipe target untuk ekspresi lambda dan digunakan pada waktu kompilasi memeriksa kode Anda.

Ketika Anda ingin menggunakannya:

1- Antarmuka Anda tidak boleh memiliki lebih dari satu metode abstrak, jika tidak, kesalahan kompilasi akan diberikan.

1- Antarmuka Anda Harus murni, yang berarti antarmuka fungsional dimaksudkan untuk diimplementasikan oleh kelas stateless, contoh Comparatorantarmuka murni adalah karena itu tidak tergantung pada negara pelaksana, dalam hal ini Tidak ada kesalahan kompilasi yang akan diberikan, tetapi dalam banyak kasus Anda tidak akan dapat menggunakan lambda dengan antarmuka semacam ini

The java.util.functionpaket berisi berbagai antarmuka fungsional tujuan umum seperti Predicate, Consumer, Function, dan Supplier.

Perlu diketahui juga bahwa Anda dapat menggunakan lambdas tanpa anotasi ini.

Ahmad Al-Kurdi
sumber
1

Selain jawaban lain, saya pikir alasan utama untuk "mengapa menggunakan Antarmuka Fungsional selain langsung dengan ekspresi lambda" dapat dikaitkan dengan sifat bahasa Jawa yang Berorientasi Objek.

Atribut utama ekspresi Lambda adalah: 1. Mereka dapat diedarkan sekitar 2. dan mereka dapat dieksekusi di masa depan dalam waktu tertentu (beberapa kali). Sekarang untuk mendukung fitur ini dalam bahasa, beberapa bahasa lain hanya berurusan dengan masalah ini.

Misalnya dalam Java Script, fungsi (fungsi Anonim, atau Fungsi literal) dapat dialamatkan sebagai objek. Jadi, Anda dapat membuatnya secara sederhana dan mereka juga dapat ditugaskan ke variabel dan sebagainya. Sebagai contoh:

var myFunction = function (...) {
    ...;
}
alert(myFunction(...));

atau melalui ES6, Anda dapat menggunakan fungsi panah.

const myFunction = ... => ...

Hingga kini, perancang bahasa Jawa belum menerima untuk menangani fitur yang disebutkan melalui cara ini (teknik pemrograman fungsional). Mereka percaya bahwa bahasa Jawa adalah Object Oriented dan oleh karena itu mereka harus menyelesaikan masalah ini melalui teknik Object Oriented. Mereka tidak ingin ketinggalan kesederhanaan dan konsistensi bahasa Jawa.

Oleh karena itu, mereka menggunakan antarmuka, seperti ketika objek antarmuka hanya dengan satu metode (maksud saya antarmuka fungsional) diperlukan, Anda dapat menggantinya dengan ekspresi lambda. Seperti:

ActionListener listener = event -> ...;
MMKarami
sumber