乐趣区

Java中的类型推断和lambda表达式

简介

java 是强类型的编程语言,每个 java 中使用到的变量都需要定义它的类型,否则会编译失败。强类型语言的好处就是可以尽可能的在编译期间就发现代码中可能出现的问题,从而减少在运行时出现问题的可能性。

相对的,强类型语言的缺点就是不那么的灵活多变,写起来比较冗余。

JDK8 之前,java 是不支持类型推断的,在 JDK8 中,引入了 lambda 表达式,从此类型推断产生了。

本文将会讲解类型推断在 lambda 表达式中的最佳实践和在使用中应该注意的事项。

更多精彩内容且看:

  • 区块链从入门到放弃系列教程 - 涵盖密码学, 超级账本, 以太坊,Libra, 比特币等持续更新
  • Spring Boot 2.X 系列教程: 七天从无到有掌握 Spring Boot- 持续更新
  • Spring 5.X 系列教程: 满足你对 Spring5 的一切想象 - 持续更新
  • java 程序员从小工到专家成神之路(2020 版)- 持续更新中, 附详细文章教程

更多内容请访问 www.flydean.com

类型的显示使用

假如我们定义了一个 CustUser 类,并且其中有 age 和 name 两个属性:

@Data
@AllArgsConstructor
public class CustUser {
    int age;
    String name;
}

看下我们怎么在 Stream 中显示使用类型:

public static void testStream(){Stream.of(new CustUser(10,"alice"), new CustUser(20,"bluce"))
                .forEach((CustUser custUser)-> System.out.println(custUser.name));
    }

上面的例子中,我们构建了一个 CustUser 类型的 Stream,并在 forEach 方法中对 CustUser 进行处理。

forEach 接收一个 Consumer 对象,Consumer 需要实现 void accept(T t) 方法。因为 Consumer 函数接口,我们可以使用 lambda 表达式来替换。

这里,我们显示传入一个 CustUser 类型。代码编译是没有问题的,但是看起来复杂了点。接下来我们看下怎么在 Stream 中使用类型推断。

Stream 中的类型推断

上面的例子,我们可以改写成这样:

public static void testInference(){Stream.of(new CustUser(10,"alice"), new CustUser(20,"bluce"))
                .forEach(custUser-> System.out.println(custUser.name));
    }

这里我们并没有定义 custUser 的类型,但是 java 可以从 Stream 中的类型推断出来。所以这样写是没有问题的,可以正常通过编译。

类型推断中变量名字的重要性

上面的例子中,我们将变量的名字定义为 custUser,查看代码的人一眼就可以看出来这个参数表示的是 CustUser 类型的 custUser 参数。

名字写的有意义可以很大程度上提升代码的可读性和可维护性。如果你这样写:

forEach(u-> System.out.println(u.name)

虽然代码变得更短了,但是失去了可读的意义,一眼看过去,大家并不知道你这个 u 代表的是什么,从而影响了代码的可读性。

所以变量名的定义一定要有意义。

类型推断对性能的影响

类型推断是个好东西,那么有同学会问了,类型推断对于 java 的性能会有影响吗?

我们可以把 java 分成编译和运行两部分。类型推断是在编译期间做的事情,可能使用类型推断会延长代码编译的时间,但是对运行时的效率是没有影响的。

一般来说,我们关注程序的性能问题是在运行时而不是编译时,所以类型推断对性能是没有影响的。

类型推断的限制

java 虽然有类型推断,但是这个推断是有一定的限制的,它并不能够像人一样去思考,但是也已经足够智能了。下面我们举个例子:

public static Comparator<CustUser> createUser1(){return (CustUser user1, CustUser user2) -> user1.getAge() - user2.getAge();
    }

上面的例子中,我们需要创建一个 Comparator,使用 lambda 表达式我们可以生成一个 Comparator。

Comparator 需要实现方法 int compare(T o1, T o2),传入两个参数,返回一个 int。

上面例子中,我们显示指定了两个参数的类型是 CustUser, 编译没有问题。

如果不显示指定 CustUser 类型可以吗?

public static Comparator<CustUser> createUser2(){return (user1, user2) -> user1.getAge() - user2.getAge();
    }

答案也是可以的。这个例子中,我们并没有传入 user1,user2,java 是怎么找到 user1 和 user2 的类型呢?

注意,上面的例子中,我们定义了返回类型是 CustUser 的,Java 通过这个返回类型来推断出传入的实际类型就是 CustUser 的。是不是很智能。

如果我们将上面的 return 语句拆分成两条,会出现问题问题呢?

Comparator comparator=(user1, user2) -> user1.getAge() - user2.getAge();

这时候就会编译报错,说找不到 getAge 方法。这是因为我们返回的 Comparator 并没有指明类型,所以默认情况下是 Object 类型。Object 类型并没有 getAge 方法,所以报错。

我们可以这样改写:

Comparator<CustUser> comparator=(user1, user2) -> user1.getAge() - user2.getAge();

编译完成,没有错误。

总结

除了 JDK8 中引入的 lambda 表示中使用了类型推断,其实 JDK10 中的 var 本地变量类型也是用到了类型推断, 详请参考 JDK10 的新特性: 本地变量类型 var。

本文的例子 [https://github.com/ddean2009/
learn-java-base-9-to-20](https://github.com/ddean2009/…

本文作者:flydean 程序那些事

本文链接:http://www.flydean.com/java-type-inference-lambda/

本文来源:flydean 的博客

欢迎关注我的公众号: 程序那些事,更多精彩等着您!

退出移动版