zoukankan      html  css  js  c++  java
  • Java8函数式接口/Lambda表达式/接口默认方法/接口静态方法/接口冲突方法重写/lambda表达式指定泛型类型等

    一:函数式接口

    1.函数式接口的概念就是此接口必须有且只能有一个抽象方法,可以通过@FunctionalInterface来显示规定(类似@Override),但是没有此注解的但是只有一个抽象方法的接口也是函数式接口;(接口也和类一样有包访问权限,但是内部的方法则默认是public)

    @FunctionalInterface
    public interface IFoo{
        void print();
    }  // 就是一个最简单的函数式接口,但是如果再有个如void print2()抽象方法则这个接口不是函数式接口会在编译时便报错(因为有@FunctionalInterface)

    2.接口里可以有静态方法(JDK8的新特性)

    @FunctionalInterface
    public interface IFoo{
        void print();
        static void sTest(){
          System.out.println("msg");
        }      
    }

    注意,接口里的静态方法默认且只能是public的,就像接口里的字段默认且只能是public static的;调用时可以直接IFoo.sTest();

    3.接口里也可以有default方法(JDK8新特性)

    @FunctionalInterface
    public interface IFoo{
        void print();
        default void sTest(){
          System.out.println("msg");
        }      
    }

    与静态方法不同的是default方法本质上是实例方法,因此仍然需要创建IFoo的实现类对象才能调用此方法,如(new IFoo(){...}).sTest();是可以的但是IFoo.sTest()则错误

    4.当一个接口成为了函数式接口后且也是以函数式接口来使用,它就类似C#里的委托,即接口和其里面的抽象方法是一一对应的,故泛型的声明要在接口名里而非抽象方法里

    @FunctionalInterface
    public interface IFoo<T extends Serializable>{
        T print(T arg);  // 写成<T> T print(T);对于将IFoo看成委托而言是错误的写法;注意<T extends B> T print。。在普通接口里也是可以的;
        static void sTest(){
          System.out.println("msg");
        }      
    }

    5.函数式接口对象和其Lambda表达式的关系

    lambda表达式不会产生里面类的.class文件,而如new Predicate(){...}是会产生的,因此如果需要将lambda表达式转换为接口对象是需要强制转换的,如:((Predicate) o -> false).and(o -> false)

    上面的(o -> false)是一个”函数对象“,它和new Predicate(){@Override test...}是不一样的,前者并没有产生Predicate的实现类,因此需要做一个强制类型转换将lambda表达式产生的结果转换为接口实现类对象;

    6.将函数式接口当成委托使用

    public interface Println<T>{
        void println(T msg);
    }

    可以Println<String> println = System.out::println;来赋值;

    7.实现多个接口但是又同名的default方法时必须重写/重定义该方法(如果是抽象方法则无所谓,当子类实现IC时实现print那么实际上即是实现IA的也是IB的不存在要重写两次的说法)

    public interface IA{
        default void print(){}
    }
    public interface IB{
        default void print(){}
    }
    public interface IC extends IA, IB{
        @Override
        default void print(){IA.super.print();}  // TODO 用于明确指定用IA的print,注意在JDK8以前由于没有default且只能继承一个类,因此只需要super.print()即可
    }

    8.lambda表达式的类型推导

    如果是非泛型自然不需要,但是如果参数是泛型,但又无法隐式推导出时可以通过(String s) ->{..}来指定泛型的类型;

    9.接口里可以都是default方法,此时这个接口可以直接new ITest{}就可以产生对象,但也可以重写default方法

    10.可以在子接口里重写父接口的default方法令其在子接口里成为抽象方法;

    11.lambda表达式和匿名内部类的区别

    lambda表达式是很特殊的存在,它不会生成.class文件,而且它也不是接口的实现类的对象,应该这么理解IFoo foo = ()->true;是IFoo能够匹配右边的lambda表达式,而不是说这个正则表达式是一个IFoo的匿名实现类的对象;

    注意上面存在一个隐式的转换即IFoo foo = (IFoo) ()->true;

    12.将方法存储到“委托”里

    ITest del = System::getenv;  // 此时通过System.getenv方法实现了ITest的抽象方法;

    del.method("string");注意,这里有几个重点,1)是ITest必须是函数式接口;2)ITest中的抽象方法名字任意且可以有泛型,但是必须能匹配System.getenv(..)这个方法的返回值和参数列表;3)del和C#的委托变量还是有些不一样的不能直接del(..)来调用,而是要del.method(..)来调用System.getenv方法;

    n)还可以用ITest t = String::new;表示String的构造方法;那么t.func(..)方法的就是new String(..)而产生的对象;(构造方法某种程度上可以理解为类的静态方法)

    n)IFoo2<String> ss = sbs::concat;,sbs是一个对象,因此ss.func(..)实际是是调用sbs.concat(..)方法

    n)对于如果方法test是类A的实例方法,则ITest del = A::test;是错误的,IDE提示没有static的test方法,因为当del.func(..)调用时将不知道要调用哪个对象的test方法,因为test的调用必须依赖某个A对象;

    补充:

    1.lambda表达式里是可以将隐藏的this参数也作为参数的,比如接口里有唯一的抽象方法String test(String str);,那么lambda表达式里可以用Object::toString作为它的参数的(toString是实例方法obj.toString(),它显示里没有参数但是实际上有个this参数);

    2.当存在方法:public <T> T execute(RedisCallback<T> action)时,用lambda表达式作为它的参数必须用(RedisCallback<Boolean>) connection -> {..}这种形式转换,这个特点是RedisCallback的T和execute的T进行了关联;

    这里之所以必须强制转换,是因为java只允许从调用处开始往下一级一级的推算泛型,比如调用处是 a,然后它可以推算出a调用的b的泛型,然后再推算出b调用的c的泛型,而不能从a里先推算出c的泛型然后再网上递推出b处的泛型;

  • 相关阅读:
    HDU1883 Phone Cell
    HDU2297 Run
    关于反射的疑惑
    struts2+spring 实例教程
    在.Net 模板页中使用CSS样式
    到底是什么反射,泛型,委托,泛型
    asp.net如何实现删除文件夹及文件内容操作
    学好C#方法
    Web网页安全色谱
    总结一些ASP.NET常用代码
  • 原文地址:https://www.cnblogs.com/silentdoer/p/8966868.html
Copyright © 2011-2022 走看看