Rumah > Java > javaTutorial > teks badan

Analisis mendalam tentang pengaturcaraan fungsi Java

WBOY
Lepaskan: 2022-11-10 16:17:22
ke hadapan
1307 orang telah melayarinya

Artikel ini membawa anda pengetahuan yang berkaitan tentang java, yang terutamanya memperkenalkan kandungan yang berkaitan tentang pengaturcaraan berfungsi Java tidak menyokong pengaturcaraan berfungsi pada mulanya, tetapi dalam java8 Untuk menyokong pengaturcaraan berfungsi dalam versi utama ini , Java telah memperkenalkan banyak ciri penting Mari kita lihat bersama-sama.

Analisis mendalam tentang pengaturcaraan fungsi Java

Kajian yang disyorkan: "tutorial video java" Fahami, kerana Class ialah warga kelas pertama di Java, yang menjadikannya tidak mudah untuk melaksanakan pengaturcaraan dalam Java. Walau bagaimanapun, walaupun sukar, kita sudah tahu hasilnya Dalam versi utama Java 8 Untuk menyokong pengaturcaraan berfungsi, Java telah memperkenalkan banyak ciri penting Dalam artikel sebelumnya, kami telah mengkaji ungkapan Lambda operasi dalam API Strim Dalam artikel hari ini, kami akan menyusun Java terbina dalam Menyediakan kami antara muka yang berfungsi.

Garis artikel ini adalah seperti berikut:

Java mengabstrak beberapa antara muka berfungsi terbina dalam untuk digunakan oleh pembangun berdasarkan kes penggunaan senario permintaan biasa, seperti

,

, dsb. Parameter atau jenis nilai pulangan pelbagai kaedah operasi dalam Strim selalunya ialah antara muka berfungsi terbina dalam ini. FunctionSupplierSebagai contoh, jenis parameter kaedah operasi peta dalam Strim ialah

Function

Jadi mengapa kita tidak pernah melihat parameter jenis ini diisytiharkan apabila kita biasanya menggunakan kaedah peta operasi Stream? Anda boleh menyemak contoh menggunakan kaedah peta dalam artikel operasi API Strim kami, seperti contoh berikut untuk menukar setiap elemen dalam strim kepada huruf besar melalui kaedah peta. Parameter
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Salin selepas log masuk
Salin selepas log masuk

kaedah peta ialah secara langsung ungkapan Lambada:
List<String> list = new ArrayList<String>();
Stream<String> stream = list.stream();

Stream<String> streamMapped = stream.map((value) -> value.toUpperCase());
Salin selepas log masuk

Ungkapan Lambda ini ialah pelaksanaan antara muka
(value) -> value.toUpperCase()
Salin selepas log masuk
.

FunctionPembawa antara muka berfungsi biasanya merupakan ungkapan Lambda Melalui ungkapan Lambda, pengkompil akan membuat kesimpulan antara muka berfungsi yang ia laksanakan berdasarkan parameter dan nilai pulangan bagi ungkapan Lambda. Menggunakan ungkapan Lambda untuk melaksanakan antara muka, kami tidak perlu menentukan antara muka untuk dilaksanakan oleh kelas seperti kelas dalaman tanpa nama Oleh itu, walaupun banyak parameter atau jenis nilai pulangan dalam operasi Stream ialah antara muka berfungsi terbina dalam Java, kami tidak melakukannya. paparkannya. Laksanakannya menggunakan kelas tanpa nama.

Walaupun ungkapan Lambda sangat mudah digunakan, ia juga menyebabkan kita sedikit keliru apabila melihat jenis antara muka berfungsi terbina dalam dalam Java "Apakah perkara ini? Apakah perkara ini?" ?" perasaan.

Mari kita bincangkan tentang pengaturcaraan berfungsi, antara muka berfungsi Java, dan sebab Lambda hanya boleh melaksanakan antara muka berfungsi Selepas menjelaskan perkara ini, kami akan menyusun antara muka berfungsi yang disediakan oleh Java.

Pengaturcaraan fungsional

Pengaturcaraan fungsional mengandungi dua konsep utama berikut:

Fungsi adalah warga kelas pertama
  • Fungsi mesti memenuhi kekangan berikut
  • Nilai pulangan fungsi hanya bergantung pada parameter input yang dihantar kepada fungsi.
    • Pelaksanaan fungsi tiada kesan sampingan.
  • Walaupun kami tidak sentiasa mematuhi semua peraturan ini semasa menulis program, kami masih boleh mendapat banyak manfaat daripada menulis program menggunakan idea pengaturcaraan berfungsi.

Seterusnya, mari kita lihat pelaksanaan dua konsep utama ini dalam pengaturcaraan berfungsi Java.

Fungsi ialah warga kelas pertama

Dalam paradigma pengaturcaraan berfungsi, fungsi ialah warga kelas pertama dalam bahasa. Ini bermakna anda boleh membuat "kejadian" fungsi dan rujukan pembolehubah kepada kejadian fungsi, sama seperti rujukan kepada Rentetan, Peta atau sebarang objek lain. Fungsi juga boleh dihantar sebagai hujah kepada fungsi lain.

Di Java, fungsi jelas bukan warga kelas pertama, kelas adalah. Itulah sebabnya Java memperkenalkan ungkapan Lambda Gula sintaksis ini membolehkan Java mempunyai fungsi dari lapisan pembentangan, membolehkan fungsi digunakan sebagai rujukan kepada pembolehubah, parameter kaedah, dsb. Mengapa anda mengatakan ia dari lapisan pembentangan? Kerana sebenarnya pengkompil Java masih akan menyusun ungkapan Lambda ke dalam kelas semasa penyusunan.

Fungsi tulen

Dalam pengaturcaraan berfungsi, terdapat konsep fungsi tulen Jika fungsi memenuhi syarat berikut, ia adalah fungsi tulen:

Pelaksanaan fungsi ini tidak mempunyai kesan sampingan.
  • Nilai pulangan fungsi bergantung hanya pada parameter input yang dihantar kepada fungsi tersebut.
  • Berikut ialah contoh fungsi tulen (kaedah) dalam Java

Nilai pulangan kaedah
public class ObjectWithPureFunction{    public int sum(int a, int b) {        return a + b;
    }
}
Salin selepas log masuk
di atas hanya bergantung pada parameter inputnya, dan

Ia tidak mempunyai kesan sampingan, ia tidak mengubah sebarang keadaan (pembolehubah) di luar fungsi di mana-mana sahaja. sum()sum()Sebaliknya, berikut ialah contoh fungsi bukan tulen:

public class ObjectWithNonPureFunction{    private int value = 0;    public int add(int nextValue) {        this.value += nextValue;        return this.value;
    }
}
Salin selepas log masuk

add()方法使用成员变量value来计算其返回值,并且它还修改了value成员变量的状态,这代表它有副作用,这两个条件都导致add方法不是一个纯函数

正如我们看到的,函数式编程并不是解决所有问题的银弹。尤其是“函数是没有副作用的”这个原则就使得在一些场景下很难使用函数式编程,比如要写入数据库的场景,写入数据库就算是一个副作用。所以,我们需要做的是了解函数式编程擅长解决哪些问题,把它用在正确的地方。

函数式接口

Java中的函数式接口在 Lambda 表达式那篇文章里提到过,这里再详细说说。函数式接口是只有一个抽象方法的接口(抽象方法即未实现方法体的方法)。一个 Interface 接口中可以有多个方法,其中默认方法和静态方法都自带实现,但是只要接口中有且仅有一个方法没有被实现,那么这个接口就可以被看做是一个函数式接口

下面这个接口只定义了一个抽象方法,显然它是一个函数式接口:

public interface MyInterface {    public void run();
}
Salin selepas log masuk

下面这个接口中,定义了多个方法,不过它也是一个函数式接口:

public interface MyInterface2 {
    public void run();

    public default void doIt() {
        System.out.println("doing it");
    }

    public static void doItStatically() {
        System.out.println("doing it statically");
    }
}
Salin selepas log masuk

因为doIt方法在接口中定义了默认实现,静态方法也有实现,接口中只有一个抽象方法run没有提供实现,所以它满足函数式接口的要求。

这里要注意,如果接口中有多个方法没有被实现,那么接口将不再是函数式接口,因此也就没办法用 Java 的 Lambda 表达式实现接口了

编译器会根据 Lambda 表达式的参数和返回值类型推断出其实现的抽象方法,进而推断出其实现的接口,如果一个接口有多个抽象方法,显然是没办法用 Lambda 表达式实现该接口的。

@FunctionalInterface 注解

这里扩充一个标注接口是函数式接口的注解@FunctionalInterface

@FunctionalInterface 
// 标明接口为函数式接口
public interface MyInterface {    public void run(); 
//抽象方法}
Salin selepas log masuk

一旦使用了该注解标注接口,Java 的编译器将会强制检查该接口是否满足函数式接口的要求:“确实有且仅有一个抽象方法”,否则将会报错。

需要注意的是,即使不使用该注解,只要一个接口满足函数式接口的要求,那它仍然是一个函数式接口,使用起来都一样。该注解只起到--标记接口指示编译器对其进行检查的作用。

Java 内置的函数式接口

Java 语言内置了一组为常见场景的用例设计的函数式接口,这样我们就不必每次用到Lambda 表达式、Stream 操作时先创建函数式接口了,Java 的接口本身也支持泛型类型,所以基本上 Java 内置的函数式接口就能满足我们平时编程的需求,我自己在开发项目时,印象里很少见过有人自定义函数式接口。

在接下来的部分中,我们详细介绍下 Java 内置为我们提供了的函数式接口。

Function

Function接口(全限定名:java.util.function.Function)是Java中最核心的函数式接口。 Function 接口表示一个接受单个参数并返回单个值的函数(方法)。以下是 Function 接口定义的:

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
            return t -> t;
    }
Salin selepas log masuk

Function接口本身只包含一个需要实现的抽象方法apply,其他几个方法都已在接口中提供了实现,这正好符合上面我们讲的函数式接口的定义:“有且仅有一个抽象方法的接口”。

Function 接口中的其他三个方法中compseandThen 这两个方法用于函数式编程的组合调用,identity用于返回调用实体对象本身,我们之前在把对象 List 转换为 Map 的内容中提到过,可以回看前面讲 List 的文章复习。

Function接口用Java 的类这么实现

public class AddThree implements Function<Long, Long> {

    @Override
    public Long apply(Long aLong) {
        return aLong + 3;
    }

    public static void main(String[] args) {
        Function<Long, Long> adder = new AddThree();
		Long result = adder.apply(4L);
		System.out.println("result = " + result);
    }
}
Salin selepas log masuk

不过现实中没有这么用的,前面说过 Lambda 表达式是搭配函数式接口使用的,用Lambda表达式实现上Function 接口只需要一行,上面那个例子用 Lambda 实现的形式是:

Function<Long, Long> adder = (value) -> value + 3;Long resultLambda = adder.apply(8L);
System.out.println("resultLambda = " + resultLambda);
Salin selepas log masuk

是不是简洁了很多。后面的接口示例统一用 Lambda 表达式举例,不再用类实现占用太多篇幅。

Function接口的常见应用是 Stream API 中的 map 操作方法,该方法的参数类型是Function接口,表示参数是一个“接收一个参数,并返回一个值的函数”。

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
Salin selepas log masuk
Salin selepas log masuk

所以我们在代码里常会见到这样使用 map 操作:

stream.map((value) -> value.toUpperCase())
Salin selepas log masuk

Predicate

Predicate 接口 (全限定名:java.util.function.Predicate)表示一个接收单个参数,并返回布尔值 true 或 false 的函数。以下是 Predicate 功能接口定义:

public interface Predicate<T> {    boolean test(T t);
}
Salin selepas log masuk

Predicate 接口里还有几个提供了默认实现的方法,用于支持函数组合等功能,这里不再赘述。 用 Lambda 表达式实现 Predicate 接口的形式如下:

Predicate predicate = (value) -> value != null;
Salin selepas log masuk

Stream API 中的 filter 过滤操作,接收的就是一个实现了 Predicate 接口的参数。

Stream<T> filter(Predicate<? super T> predicate);
Salin selepas log masuk

写代码时,会经常见到这样编写的 filter 操作:

Stream<String> longStringsStream = stream.filter((value) -> {    
// 元素长度大于等于3,返回true,会被保留在 filter 产生的新流中。
    return value.length() >= 3;
});
Salin selepas log masuk

Supplier

Supplier 接口(java.util.function.Supplier),表示提供某种值的函数。其定义如下:

@FunctionalInterfacepublic interface Supplier<T> {
    T get();
}
Salin selepas log masuk

Supplier接口也可以被认为是工厂接口,它产生一个泛型结果。与 Function 不同的是,Supplier 不接受参数。

Supplier<Integer> supplier = () -> new Integer((int) (Math.random() * 1000D));
Salin selepas log masuk

上面这个 Lambda 表达式的 Supplier 实现,用于返回一个新的 Integer 实例,其随机值介于 0 到 1000 之间。

Consume

Consumer 接口(java.util.function.Consume)表示一个函数,该函数接收一个参数,但是不返回任何值。

@FunctionalInterfacepublic interface Consumer<T> {    void accept(T t);
}
Salin selepas log masuk

Consumer 接口常用于表示:要在一个输入参数上执行的操作,比如下面这个用Lambda 表达式实现的 Consumer,它将作为参数传递给它的value变量的值打印到System.out标准输出中。

Consumer<Integer> consumer = (value) -> System.out.println(value);
Salin selepas log masuk

Stream API 中的 forEach、peek 操作方法的参数就是 Consumer 接口类型的。

Stream<T> peek(Consumer<? super T> action);
void forEach(Consumer<? super T> action);
Salin selepas log masuk

比如,Stream API 中的 forEach 操作,会像下面这样使用 Consume 接口的实现

Stream<String> stream = stringList.stream();
// 下面是Lambda 的简写形式
// 完整形式为:value -> System.out.println(value);
stream.forEach(System.out::println);
Salin selepas log masuk

Optional

最后再介绍一下 Optional 接口,Optional 接口并不是一个函数式接口,这里介绍它主要是因为它经常在一些 Stream 操作中出现,作为操作的返回值类型,所以趁着学习函数式编程的契机也学习一下它。

Optional 接口是预防NullPointerException的好工具,它是一个简单的容器,其值可以是 null 或非 null。比如一个可能返回一个非空结果的方法,方法在有些情况下返回值,有些情况不满足返回条件返回空值,这种情况下使用 Optional 接口作为返回类型,比直接无值时返回 Null 要更安全。 接下来我们看看 Optional 怎么使用:

// of 方法用于构建一个 Optional 容器
Optional<String> optional = Optional.of("bam");
// 判断值是否为空
optional.isPresent();           // true
// 取出值,如果不存在直接取会抛出异常
optional.get();                 // "bam"
// 取值,值为空时返回 orElse 提供的默认值
optional.orElse("fallback");    // "bam"
// 如果只存在,执行ifPresent参数中指定的方法
optional.ifPresent((s) -> System.out.println(s.charAt(0)));// "b"
Salin selepas log masuk

Stream 操作中像 findAny、 findFirst这样的操作方法都会返回一个 Optional 容器,意味着结果 Stream 可能为空,因此没有返回任何元素。我们可以通过 Optional 的 isPresent() 方法检查是否找到了元素。Java 编程那些绕不开的接口 这个子系列的文章已经更新完毕,感兴趣的请持续关注,后面还有更多实用、精彩的内容。


推荐学习:《java视频教程

Atas ialah kandungan terperinci Analisis mendalam tentang pengaturcaraan fungsi Java. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Label berkaitan:
sumber:juejin.im
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan