zoukankan      html  css  js  c++  java
  • JAVA基础之接口与内部类

    接口与内部类

    本文主要整理了一些作者看JAVA核心技术卷第六章遇到的难点以及其思考, 欢迎小伙伴及时指出错误!

    1. Lambda表达式

    1. 关于懒计算

    在JAVA8中, 提供了 Supplier这个接口实现懒计算

    原理是这样的, 主要依据是以下三个原理

    • 在JAVA8的新特性中, 只要一个接口只有一个抽象方法(不包括default和static), 那么这个接口就会被被认为是一个函数式接口, 可以使用lambda表达式, 而注解 @FunctionalInterface 和我们的 @Override 一样, 用于提示, 不写也可以, 但是建议写

    • lambda表达式在被调用时才执行

    • lambda表达式可以做类型推断(不是太重要的原理)

    我们可以观察Objects.requireNoNull 方法, 在参数为 null 会抛出一个异常, 异常的内容与我们传递的第二个参数有关

    这个方法有三个重载, 我们主要关注的是有两个方法的重载

    • 首先是传统的重载
    public static <T> T requireNonNull(T obj, String message) {
        if (obj == null)
            throw new NullPointerException(message);
        return obj;
    }
    

    这里直接传入了一个String类型的message, 这样看虽然没什么不妥, 但是设想一下, 如果我们的 null 不是一个经常出现的结果, 同时我们的String是通过调用某个方法得到的, 这样每次执行非空判断, 都会调用我们对message写的方法, 比如我们传入一个时间

    new LocalDate(1970, 1, 1);
    

    这样如果有大量的进程调用这个判断, 同时并没有那么多null, 会造成性能浪费

    • 基于懒计算的优化
    public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
        if (obj == null)
            throw new NullPointerException(messageSupplier.get());
        return obj;
    }
    

    与上面不同, 这里的第二个参数是一个 Supplier 接口, 我们从第一点可以得知, 由于该接口只有一个抽象方法, 因此它是一个函数式接口, 我们可以使用Lambda表达式; 又根据我们第二点, lambda表达式只有在被调用的时候才会执行, 那么如果我们没有那么多的空判断, 这个方法就不会执行, 当我们的第二个参数很复杂(比如要向数据库查询数据), 这样就可以节省了大量的性能, 第二个参数的lambda表达式我们可以这样写

    () -> new LocalDate(1970, 1, 1)
    

    类似的, 与懒计算设计思路相似的优化方法还有懒加载, 即页面的元素(比如图片或者视频等)只有在被调用(比如我们往下翻页的时候)才加载, 这样大大缓解了服务器的压力与网络的压力, 毕竟不是所有人都会看到底的

    2. Predicate接口

    @FunctionalInterface
    public interface Predicate<T> {
    
        boolean test(T t);
    
        default Predicate<T> and(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) && other.test(t);
        }
    
        default Predicate<T> negate() {
            return (t) -> !test(t);
        }
        
        default Predicate<T> or(Predicate<? super T> other) {
            Objects.requireNonNull(other);
            return (t) -> test(t) || other.test(t);
        }
    
        static <T> Predicate<T> isEqual(Object targetRef) {
            return (null == targetRef)
                    ? Objects::isNull
                    : object -> targetRef.equals(object);
        }
    

    可以看出, 这个接口同样只有一个抽象方法, 因此他也是一个函数式接口, 这个函数式接口很有用, 因为它可以返回一个布尔值, 在我们传入一个方法可以做判断

    3. 关于方法引用

    • 方法引用主要有三种情况

      • object :: instanceMethod 等价于向方法传递参数的lambda表达式
      • Class :: instanceMethod 等价于第一个参数作为方法的隐式参数(即this, 表示该参数自己, 方法包括定义自己的属性或者调用自己的一些方法, 最后的结果会返回到这个参数上), 其余的参数会传递到方法
      • Class :: staticMethod 等价于所有的参数都传递到静态方法中, 与上面的区别是有没有隐式参数(即改变了自己的值)
    • 虽然我们可以用lambda表达式来等价方法引用, 但是两者最重要的区别是 方法引用会立即执行, 而lambda表达式只有在调用的时候才会执行

    • 只有当lambda表达式的方法体只调用一个方法而不做其他操作时, 我们才可以将lambda表达式重写为方法引用, 比如下面的就不可以, 因为它除了方法调用, 还进行了比较

    s -> s.length() == 0
    

    4. 关于构造器引用

    • 构造器引用的基本结构

      • Class :: new
      • 表示 Class 构造器的一个引用, 引用的构造器取决于上下文, 编译器会自动推导
    • 数组类型的构造器引用

      • Class[] :: new

      • 等价于

        x -> new Class[x]
        

        即创建了一个指定类型的对象数组

    5. 关于变量的作用域

    • lambda可以捕获外围作用域中的变量的值
    • lambda表达式中捕获的值必须实际上是 事实最终变量, 即初始化后就不再为其赋新值, 这是由于lambda表达式在调用后才执行, 如果改变的话会造成不安全
    • lambda表达式的体与嵌套块有相同的作用域, 我们可以理解为, 在lambda表达式左侧传入的变量和上下文的变量的作用域是一致的
      • 在lambda表达式中, 没与参数也要写括号 () -> xxx
      • 在lambda表达式中, 会自动推断变量类型, 可以不写, (String first) -> xxx 和 (first) -> xxx是一样的, 因此如果上文有first这个变量, 这里就会报变量冲突的错误

    2. 内部类

    1. 局部内部类

    • 在一个方法中局部定义的类叫做局部内部类

    • 声明局部类时不能有访问说明符(即 public private protected), 作用域仅限于声明这个局部类的类中 ==> 可以访问类的全部属性, 包括私有属性

    • 优点: 对外部世界完全屏蔽

    2. 匿名内部类

    • 如果只想创建局部内部类的一个对象而不需要给其指定名字, 可以使用匿名内部类

    • new SuperType(construction parameters) 
      {
          inner class methods and data
      }
      
    • SuperType可以是接口 ==> 匿名内部类实现这个接口

    • SuperType可以使一个类 ==> 匿名内部类拓展这个类

    • 如果参数列表的结束小括号后面跟着一个开始大括号, 就是在定义匿名内部类

    • 与lambda表达式最大的区别

      • lambda编译后不会生成class文件,那么也就略过了类的加载、验证、解析等。相当于是在运行时再进行相应的操作
      • 这里主要体现在对Spring的影响中, 在spring注入过程中,无法注入含确定类型的入参和出参方法的实现类,所以,才会出现无法确定类型,导致注入失败,从而springboot启动失败的问题。
  • 相关阅读:
    CodeForces Gym 100935G Board Game DFS
    CodeForces 493D Vasya and Chess 简单博弈
    CodeForces Gym 100935D Enormous Carpet 快速幂取模
    CodeForces Gym 100935E Pairs
    CodeForces Gym 100935C OCR (水
    CodeForces Gym 100935B Weird Cryptography
    HDU-敌兵布阵
    HDU-Minimum Inversion Number(最小逆序数)
    七月馒头
    非常可乐
  • 原文地址:https://www.cnblogs.com/wang-sky/p/13994768.html
Copyright © 2011-2022 走看看