工作笔记——JAVA:函数式接口学习(::语法、双冒号语法)

一个絮叨的工作笔记,不想看废话的直接从 自定义@FunctionalInterface 往下看吧

起因

最近在学习spring-boot源码的时候发现了一个特殊的语法。

	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
		// 获得 ServletContextInitializer 对象
		return this::selfInitialize;
	}

懵逼了好久,后才查询了之后才知道上面内容等价于

return new ServletContextInitializer() {

            @Override
            public void onStartup(ServletContext servletContext) throws ServletException {
                selfInitialize(servletContext);           
            }
        };
}

新的问题

为什么会等价于这个呢?对于程序员永远要对新的事物抱有兴趣,然后我就去找了找这个语法还有什么别的用处。

一个简单的demo

首先根据网上的说法::的作用是,左边是类,又变是类中的方法,简单的例子

public class DaiTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("1","2","3","4");
        // 在循环中将每个循环出来的内容,调用DaiTest.print的方法
        list.forEach(DaiTest::print);
    }
    
    public static void print(String s) {
        System.out.print(s);
    }
}

执行的结果是

1234

java.util.function

使用IDEA的时候点击::会发现跳转到了一个新的接口上,会发现这就是函数式接口

@FunctionalInterface
public interface Consumer<T> {

    /**
     *
     * @param t the input argument
     */
    void accept(T t);

    /**
     *
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

所以发现之前的代码

public static void main(String[] args) {
        List<String> list = Arrays.asList("1","2","3","4");
        // 在循环中将每个循环出来的内容,调用DaiTest.print的方法
        list.forEach(DaiTest::print);
    }

等价于

public static void main(String[] args) {
        List<String> list = Arrays.asList("1","2","3","4");
        // 在循环中将每个循环出来的内容,调用DaiTest.print的方法
        Consumer<String> consumer = DaiTest::print;
        list.forEach(consumer);
    }

工作笔记——JAVA:函数式接口学习(::语法、双冒号语法)_第1张图片

可以看出来可以根据输入参数和输出参数的不同返回不同的function方法,那么我们看看到底什么是函数式接口

@FunctionalInterface

Java 8为函数式接口引入了一个新注解@FunctionalInterface,主要用于编译级错误检查,加上该注解,当你写的接口不符合函数式接口定义的时候,编译器会报错。

@FunctionalInterface注释的约束同时也是函数式接口的约束

  • 接口有且只能有个一个抽象方法,只有方法定义,没有方法体
  • 在接口中覆写Object类中的public方法,不算是函数式接口的方法。

看文字实在是不知道怎么理解,那么下面我们做几个例子看看。

自定义@FunctionalInterface

首先我们有个类,有如下方法

public class DaiTest {

    public String getStr(String str) {
        System.out.println("接收的参数:" + str);
        return "456";
    }

    public Boolean getBo(String str) {
        return false;
    }

    public Integer getInt(String str) {
        System.out.println("接收的参数:" + str);
        return 1;
    }

    public Long getLong(String str) {
        return 1L;
    }

    public Object getOb(String str) {
        return new Object();
    }

    public DaiTest() {
        System.out.println("开始创建DaiTest");
    }
}

创建3个@FunctionalInterface,分别对应getStr、getInt和构造函数的入参、出参

@FunctionalInterface
public interface TestFunction {

    String getfunction(String str);
}

@FunctionalInterface
public interface TestIntFunction {

    Integer getfunction(String str);
}

@FunctionalInterface
public interface TestObjFunction {

    DaiTest getfunction();
}


然后执行main方法中的代码


public static void main(String[] args) {
        DaiTest test = new DaiTest();

        TestFunction testFunction = test::getStr;
        TestIntFunction getInt = test::getInt;
        TestObjFunction aNew = DaiTest::new;

        String testFunction1 = testFunction.getfunction("testFunction");
        System.out.println("运行TestFunction结束");
        Integer getInt1 = getInt.getfunction("getInt");
        System.out.println("运行TestIntFunction结束");
        DaiTest getfunction = aNew.getfunction();
        System.out.println("运行TestObjFunction结束");

    }

最后返回结果是

接收的参数:testFunction
运行TestFunction结束
接收的参数:getInt
运行TestIntFunction结束
开始创建DaiTest
运行TestObjFunction结束

可以看到我们调用接口的方法,实际上调用的是DaiTest类方法的引用

总结

  1. ::(双冒号)的语法, 冒号左边定义了类,右边定义了类对应的方法,指向到一个符合函数式的接口。
    1. 需要注意的几个,方法假如非静态类,则类需要首先实例化出来。就像正常方法调用,你不可能直接调用类的非静态化方法
    2. 此时你调用函数式接口的方法实际调用的逻辑是,引用的方法。
  2. 符合函数式的接口的定义:
    1. 接口有且只能有一个抽象方法。
    2. 接口中可以重写Object的方法,不计算到方法数量限制中。
    3. @FunctionalInterface不是必须的,对的@FunctionalInterface注解只是方便编译时候校验,接口只要符合函数式即使没有注解也是可以的。(不过建议能加上注解还是加上)。
    4. 接口的入参和出参要和引用的方法一致。
    5. 除了重写Object方法,在接口中通过default实现的方法也不计算到方法数量限制中。
  3. 目前个人这个功能接触的比较少,还不清楚哪种环境种使用这种语法会方便些 ε=(´ο`*)))唉。

你可能感兴趣的:(工作随笔)