Java8和Scala中的Lambda表达式

Java8和Scala中的Lambda表达式,第1张

    Java 终于要支持Lambda表达式!自 年以来Lambda表达式已经在Lambda项目中被支持 在那时候 Lambda表达式仍被称为Java闭包 在我们进入一些代码示例以前 先来解释下为什么Lambda表达式在Java程序员中广受欢迎         为什么使用Lambda表达式        Lambda表达式通常使用在图形用户界面(GUI)的开发中 一般来说 GUI编程将程序行为和事件做连接 比如 当用户按下一个按钮(触发一个事件) 你的程序就需要去执行某些行为 可能是将一些数据储存到一个数据存储器中 在Swing中 可以使用ActionListener来实现         class ButtonHandler implements ActionListener {        public void actionPerformed(ActionEvent e)        {        //do something        }        }        class UIBuilder {        public UIBuilder() {        button addActionListener(new ButtonHandler())         }        }        这个例子表明了 ButtonHandler 类作为一个回调替换的用法 在这里 ButtonHandler 类仅包含 ActionListener 接口定义的 actionPerformed 方法 我们可以使用匿名内部类来简化代码         class UIBuilder {        public UIBuilder() {        button addActionListener(new ActionListener() {        public void actionPerformed(ActionEvent event) {        //do something        }        })        }        }        这样代码简洁多了 更仔细的去看代码时 就会发现我们还创建一个只生成一个实例的类 而这个实例也仅仅持有一个独立的方法 这恰好是Lambda表达式所能解决的其中一类问题         Lambda表达式代替函数        一个lambda表达式从字面上讲就是一个函数 它定义了一个函数的输入参数和函数体 Java 中的 lambda表达式语法尚未确定 不过大致应该类似这个样子的         (type parameter) > function_body 一个具体的例子         (String s String s ) > s length() s length()         这个lambda表达式用来计算两个字符串的长度差 还有一些扩展的语法 比如避免参数的类型定义(我们马上见看到例子)还有使用{和}来支持多行定义         Collections sort()        方法是lambda表达的理想例子 它允许我们将字符串按照长度排序         List<String> list = Array asList( loooooong short tiny )         Collections sort(list (String s String s ) > s length() s length())         > tiny short loooooong         所以 不像现在java必须要求的向sort方法输入一个已经实现的Comparator(比较器)而是传送一个lambda表达式我们就可以得到相同的结果         Lambda表达式代替闭包        lambda表达式有许多有趣的特性 其中之一是 它们是闭包 一个闭包允许函数访问直接词法作用域之外的变量         String outer = java (String s ) > s length() outer length()        在例子中 lambda表达式访问了字符串 outer 这个作用域之外定义的变量 对于内联闭包来说这是很难做到的         Lambda表达式也支持类型推论        类型推论是java 引入的但它同样适用于lambda表达式 简单来说 类型推论意味着程序员可以在任意一个编译器能够自动推断出类型的地方省略类型定义 如果类型推论能够应用到前面的排序lambda表达式上 那么它就能写成下面的样子         List<String> list = Arrays asList(…)         Collections sort(list (s s ) > s length() s length())         就像你所见到的一样 参数s 和s 的类型被省略了 因为编译器知道list是一个字符串集合 它知道被用来作为比较器的lambda表达式必定是相同的类型 因此 这个类型不需要显式地声明 即使你有这么做的自由         类型推论的主要优势就是减少样板代码 如果编译器可以为我们识别类型 为什么我们必须自己定义它们         珍爱Lambda表达式 远离匿名内部类        我们来体会下 为何lambda表达式和类型推论有助于简化我们前面所提到的回调例子         class UIBuilder {        public UIBuilder() {        button addActionListener(e > //process ActionEvent e)        }        }        我们下载直接传送一个lambda表达式进入 addActionListener 方法来代替前面定义的持有回调方法的类 除了减少模板代码和提高可读性以外 它使我们直接表达我们唯一感兴趣的事情 处理事件         在我们了解lambda表达式更多优势之前 先来看看在Scala中的lambda表达式副本         Scala中的Lambda表达式        在函数式编程中 函数是基本的构造块 Scala融合了java中的面向对象编程和函数式编程 在Scala中 一个lambda表达式是种叫做 函数 或者 函数文本 Scala中的函数属于一等公民 它们可以被分配给vals或者vars(最终变量或者非最终变量) 它们可以作为其他函数的参数 也可以组合成新的函数         在Scala中一个函数文本写成如下形式         (argument) => //funtion body        举例来说 前面提到的java 用来计算两个字符串长度差的 lambda 表达式 在Scala中写作如下         (s : String s :String) => s length s         length Scala中的函数文本也是闭包 它可以访问在直接词法作用域之外定义的变量         val outer = val myFuncLiteral = (y: Int) => y outer  val result = myFuncLiteral( )  > 这个例子结果是         正如你所见 我们将函数文本分配给了变量 myFuncLiteral         java 的lambda表达式和Scala的函数文本在语法和语义上的相似性是十分明显的 从语义上讲它们是相同的 而语法上的唯一不同就是箭头符号(java > scala =>)和我们没有提到的简化符号 lishixinzhi/Article/program/Java/hx/201311/27132

1、函数式接口

Java 8 引入的一个核心概念是函数式接口(Functional Interfaces)。通过在接口里面添加一个抽象方法,这些方法可以直接从接口中运行。如果一个接口定义个唯一一个抽象方法,那么这个接口就成为函数式接口。同时,引入了一个新的注解:@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口。这个注解是非必须的,只要接口只包含一个方法的接口,虚拟机会自动判断,不过最好在接口上使用注解 @FunctionalInterface 进行声明。在接口中添加了 @FunctionalInterface 的接口,只允许有一个抽象方法,否则编译器也会报错。

javalangRunnable 就是一个函数式接口。

@FunctionalInterface

public interface Runnable {

public abstract void run();

}

2、Lambda 表达式

函数式接口的重要属性是:我们能够使用 Lambda 实例化它们,Lambda 表达式让你能够将函数作为方法参数,或者将代码作为数据对待。Lambda 表达式的引入给开发者带来了不少优点:在 Java 8 之前,匿名内部类,监听器和事件处理器的使用都显得很冗长,代码可读性很差,Lambda 表达式的应用则使代码变得更加紧凑,可读性增强;Lambda 表达式使并行 *** 作大集合变得很方便,可以充分发挥多核 CPU 的优势,更易于为多核处理器编写代码;

Lambda 表达式由三个部分组成:第一部分为一个括号内用逗号分隔的形式参数,参数是函数式接口里面方法的参数;第二部分为一个箭头符号:->;第三部分为方法体,可以是表达式和代码块。语法如下:

1 方法体为表达式,该表达式的值作为返回值返回。

(parameters) -> expression

2 方法体为代码块,必须用 {} 来包裹起来,且需要一个 return 返回值,但若函数式接口里面方法返回值是 void,则无需返回值。

(parameters) -> { statements; }

例如,下面是使用匿名内部类和 Lambda 表达式的代码比较。

下面是用匿名内部类的代码:

buttonaddActionListener(new ActionListener() {

@Override

public void actionPerformed(ActionEvent e) {

Systemoutprint("Helllo Lambda in actionPerformed");

}

});

下面是使用 Lambda 表达式后:

buttonaddActionListener(

\\actionPerformed 有一个参数 e 传入,所以用 (ActionEvent e)

(ActionEvent e)->

Systemoutprint("Helllo Lambda in actionPerformed")

);

上面是方法体包含了参数传入 (ActionEvent e),如果没有参数则只需 ( ),例如 Thread 中的 run 方法就没有参数传入,当它使用 Lambda 表达式后:

Thread t = new Thread(

\\run 没有参数传入,所以用 (), 后面用 {} 包起方法体

() -> {

Systemoutprintln("Hello from a thread in run");

}

);

通过上面两个代码的比较可以发现使用 Lambda 表达式可以简化代码,并提高代码的可读性。

为了进一步简化 Lambda 表达式,可以使用方法引用。例如,下面三种分别是使用内部类,使用 Lambda 表示式和使用方法引用方式的比较:

//1 使用内部类

Function<Integer, String> f = new Function<Integer,String>(){

@Override

public String apply(Integer t) {

return null;

}

};

//2 使用 Lambda 表达式

Function<Integer, String> f2 = (t)->StringvalueOf(t);

//3 使用方法引用的方式

Function<Integer, String> f1 = String::valueOf;

要使用 Lambda 表达式,需要定义一个函数式接口,这样往往会让程序充斥着过量的仅为 Lambda 表达式服务的函数式接口。为了减少这样过量的函数式接口,Java 8 在 javautilfunction 中增加了不少新的函数式通用接口。例如:

Function<T, R>:将 T 作为输入,返回 R 作为输出,他还包含了和其他函数组合的默认方法。

Predicate<T> :将 T 作为输入,返回一个布尔值作为输出,该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(与、或、非)。

Consumer<T> :将 T 作为输入,不返回任何内容,表示在单个参数上的 *** 作。

例如,People 类中有一个方法 getMaleList 需要获取男性的列表,这里需要定义一个函数式接口 PersonInterface:

interface PersonInterface {

public boolean test(Person person);

}

public class People {

private List<Person> persons= new ArrayList<Person>();

public List<Person> getMaleList(PersonInterface filter) {

List<Person> res = new ArrayList<Person>();

personsforEach(

(Person person) ->

{

if (filtertest(person)) {//调用 PersonInterface 的方法

resadd(person);

}

}

);

return res;

}

}

为了去除 PersonInterface 这个函数式接口,可以用通用函数式接口 Predicate 替代如下:

class People{

private List<Person> persons= new ArrayList<Person>();

public List<Person> getMaleList(Predicate<Person> predicate) {

List<Person> res = new ArrayList<Person>();

personsforEach(

person -> {

if (predicatetest(person)) {//调用 Predicate 的抽象方法 test

resadd(person);

}

});

return res;

}

}

3、接口的增强

Java 8 对接口做了进一步的增强。在接口中可以添加使用 default 关键字修饰的非抽象方法。还可以在接口中定义静态方法。如今,接口看上去与抽象类的功能越来越类似了。

默认方法

Java 8 还允许我们给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做扩展方法。在实现该接口时,该默认扩展方法在子类上可以直接使用,它的使用方式类似于抽象类中非抽象成员方法。但扩展方法不能够重载 Object 中的方法。例如:toString、equals、 hashCode 不能在接口中被重载。

例如,下面接口中定义了一个默认方法 count(),该方法可以在子类中直接使用。

public interface DefaultFunInterface {

//定义默认方法 countdefault int count(){

return 1;

}

}

public class SubDefaultFunClass implements DefaultFunInterface {

public static void main(String[] args){

//实例化一个子类对象,改子类对象可以直接调用父接口中的默认方法 count

SubDefaultFunClass sub = new SubDefaultFunClass();

subcount();

}

}

静态方法

在接口中,还允许定义静态的方法。接口中的静态方法可以直接用接口来调用。

例如,下面接口中定义了一个静态方法 find,该方法可以直接用 StaticFunInterface find() 来调用。

public interface StaticFunInterface {public static int find(){

return 1;

}

}

public class TestStaticFun {

public static void main(String[] args){

//接口中定义了静态方法 find 直接被调用

StaticFunInterfacefine();

}

}

并不是所有接口都可以使用Lambda表达式,只有函数式接口可以。

按照Java8函数式接口的定义,其只能有一个抽象方法,否则就不是函数时接口,就无法用Lambda表达式。

可以使用@FunctionalInterface标注函数式接口,在编译时提前发现错误。

你只需要知道两点

堆中存放具体数据

栈中存放你所命名的变量名字

既然叫匿名函数

自然没有名字

所以这就是在堆里有具体数据

而栈中没有名字指向这个数据的意思

Java 8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。下面这个例子就是使用Lambda语法来代替匿名的内部类,代码不仅简洁,而且还可读。

没有使用Lambda的老方法: buttonaddActionListener(new ActionListener(){public void actionPerformed(ActionEvent ae){Systemoutprintln(Actiondetected);}});使用Lambda: buttonaddActionListener(()->{Systemoutprintln(Actiondetected);});让我们来看一个更明显的例子。

不采用Lambda的老方法: Runnable runnable1=new Runnable(){@Overridepublic void run(){Systemoutprintln(RunningwithoutLambda);}};使用Lambda: Runnable runnable2=()->{Systemoutprintln(RunningfromLambda);};正如你所看到的,使用Lambda表达式不仅让代码变的简单、而且可读、最重要的是代码量也随之减少很多。然而,在某种程度上,这些功能在Scala等这些JVM语言里已经被广泛使用。

并不奇怪,Scala社区是难以置信的,因为许多Java 8里的内容看起来就像是从Scala里搬过来的。在某种程度上,Java 8的语法要比Scala的更详细但不是很清晰,但这并不能说明什么,如果可以,它可能会像Scala那样构建Lambda表达式。

一方面,如果Java继续围绕Lambda来发展和实现Scala都已经实现的功能,那么可能就不需要Scala了。另一方面,如果它只提供一些核心的功能,例如帮助匿名内部类,那么Scala和其他语言将会继续茁壮成长,并且有可能会凌驾于Java之上。其实这才是最好的结果,有竞争才有进步,其它语言继续发展和成长,并且无需担心是否会过时。

jdk版本迭代都是根据上一代进行增添新功能。djk11在18版本上只是添加了少许新内容以适应现在互联网du技术节奏,除了新添加的内容,两者没有什么影响。也就是,如果不用到新添加的内容,运行不受影响。但是需要知道,有哪些内容是新的。

JDK18的新特性:

一、接口的默认方法Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法。

二、Lambda 表达式在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:

Collectionssort(names, (String a, String b) -> {return bcompareTo(a);});

三、函数式接口Lambda表达式是如何在java的类型系统中表示的,每一个lambda表达式都对应一个类型,通常是接口类型。

而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为默认方法不算抽象方法,所以也可以函数式接口添加默认方法。

四、方法与构造函数引用Java 8 允许你使用 :: 关键字来传递方法或者构造函数引用,上面的代码展示了如何引用一个静态方法,我们也可以引用一个对象的方法:

converter = something::startsWith;

String converted = converterconvert("Java");

Systemoutprintln(converted);

五、Lambda 作用域在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。

六、访问局部变量可以直接在lambda表达式中访问外层的局部变量:

七、访问对象字段与静态变量 和本地变量不同的是,lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的:

八、访问接口的默认方法JDK 18 API包含了很多内建的函数式接口,在老Java中常用到的比如Comparator或者Runnable接口,这些接口都增加了@FunctionalInterface注解以便能用在lambda上。

Java 8 API同样还提供了很多全新的函数式接口来让工作更加方便,有一些接口是来自Google Guava库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到lambda上使用的。

扩展资料:

jdk11新特性:

1、字符串加强

// 判断字符串是否为空白" "isBlank(); // true// 去除首尾空格" Javastack "strip(); // "Javastack"// 去除尾部空格 " Javastack "stripTrailing()。

// 去除首部空格 " Javastack "stripLeading(); // "Javastack "// 复制字符串"Java"repeat(3); // "JavaJavaJava"// 行数统计"A\nB\nC"lines()count(); // 3

2、HttClient Api

这是 Java 9 开始引入的一个处理 HTTP 请求的的孵化 HTTP Client API,该 API 支持同步和异步,而在 Java 11 中已经为正式可用状态,你可以在javanet包中找到这个 Api

3、用于 Lambda 参数的局部变量语法

用于 Lambda 参数的局部变量语法简单来说就是支持类型推导:

var x = new A();for (var x : xs) { }try (var x = ) { } catch

4、ZGC

从JDK 9开始,JDK使用G1作为默认的垃圾回收器。G1可以说是GC的一个里程碑,G1之前的GC回收,还是基于固定的内存区域,而G1采用了一种“细粒度”的内存管理策略,不在固定的区分内存区域属于surviors、eden、old。

而我们不需要再去对于年轻代使用一种回收策略,老年代使用一种回收策略,取而代之的是一种整体的内存回收策略。

这种回收策略在我们当下cpu、内存、服务规模都越来越大的情况下提供了更好的表现,而这一代ZGC更是有了突破性的进步。

从原理上来理解,ZGC可以看做是G1之上更细粒度的内存管理策略。由于内存的不断分配回收会产生大量的内存碎片空间,因此需要整理策略防止内存空间碎片化。

在整理期间需要将对于内存引用的线程逻辑暂停,这个过程被称为"Stop the world"。只有当整理完成后,线程逻辑才可以继续运行。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/12188269.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-21
下一篇 2023-05-21

发表评论

登录后才能评论

评论列表(0条)

保存