lambda表达式 、匿名内部类实现功能接口、::方法引用 三者之间的关系

匿名内部类

参考

定义

匿名内部类没有名字, 使用匿名内部类我们必须要且只能继承一个父类或者实现一个接口

同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。

new 父类构造器(参数列表) | 实现接口(){  
//匿名内部类的类体部分  
}

注意

1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

2、匿名内部类中是不能定义构造函数的。

3、匿名内部类中不能存在任何的静态成员变量和静态方法。

4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

匿名内部类使用的形参要为final

当所在的方法的形参需要被内部类里面使用时,该形参必须为final。

public class OuterClass {
    public void display(final String name,String age){
        class InnerClass{
            void display(){
                System.out.println(name);
            }
        }
    }
}

从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下:

public class OuterClass$InnerClass {
    public InnerClass(String name,String age){
        this.InnerClass$name = name;
        this.InnerClass$age = age;
    }
    
    
    public void display(){
        System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );
    }
}

内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的参数进行备份,自己内部方法调用的实际上时自己的属性而不是外部方法传递进来的参数。

匿名内部类如何初始化

通过构造代码块

public InnerClass getInnerClass(final int age,final String name){
    return new InnerClass() {
        int age_ ;
        String name_;
        //构造代码块完成初始化工作
        {
            if(0 < age && age < 200){
                age_ = age;
                name_ = name;
            }
        }
        public String getName() {
            return name_;
        }

        public int getAge() {
            return age_;
        }
    };
}

::方法引用

参考/解释

::方法引用: 以lambda方式调用一个已经存在的方法

使用lambda表达式会创建匿名方法,但有时候需要使用一个lambda表达式只调用一个已经存在的方法,所以这才有了方法引用!

Java 8 中我们可以通过 :: 关键字来访问类的构造方法,对象方法,静态方法。

可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,::关键字可以使语言更简洁,减少冗余代码。

语法种类 示例
引用静态方法 ContainingClass::staticMethodName
引用特定对象的实例方法 containingObject::instanceMethodName
引用特定类型的任意对象的实例方法 ContainingType::methodName
引用构造函数 ClassName::new

lambda表达式

定义

lambda表达式=匿名内部类实现功能接口=::方法引用

Java中lambda表达式的唯一用途是将它们转换为功能接口,使用lambda表达式也可以看作函数式接口(功能接口)的一个实现子类。如果方法接受功能接口(函数接口)则可以传递lambda表达式,这进一步意味着,可以将lambda传递给所有接受方法的现有方法ComparatorRunnable或任何其他只有一种抽象方法的接口(功能接口)

(paramters) -> {statements;}  
展开如:
(Type1 param1, Type2 param2, Type2 param2, ...) -> {
    statement1;
    statement2;
    statement3;
    ...
    return statementX;
}

功能接口:

具有唯一一种抽象方法的接口称为功能接口。 添加了@FunctionalInterface批注,以便我们可以将接口标记为功能接口。

Java 8功能接口的主要好处是我们可以使用lambda表达式实例化它们(相当于implements接口),并避免使用笨重的匿名类实现。

java.lang.Runnable是具有单个抽象方法run()的功能接口的一个很好的示例。

表达式特征

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

举例

例1

例2

//入参为空
TestDemo no_param = () -> "hi, no param";
TestDemo no_param2 = () -> { return "hi, no param"; };
System.out.println(no_param.hi());

//单个参数
TestDemo2 param = name -> name;
TestDemo2 param2 = name -> { return name;};
System.out.println(param.hei("hei, grils"));

//多个参数
TestDemo3 multiple = (String hello, String name) -> hello + " " + name;
//一条返回语句,可以省略大括号和return
TestDemo3 multiple2 = (hello, name) -> hello + name;
//多条处理语句,需要大括号和return
TestDemo3 multiple3 = (hello, name) -> {
    System.out.println("进入内部");
    return hello + name;
};
System.out.println(multiple.greet("hello", "lambda"));
posted @ 2021-07-23 11:19  身在迷途  阅读(374)  评论(0)    收藏  举报