Java进阶:接口、lambda表达式与内部类

 

1.接口示例

  1)接口与回调

            ①回调:可以指出某个特定事件发生时应该采取的动作。

               例:java.swing.Timer可以使用它在到达给定的时间间隔时发出通告。

               在构造定时器时,需要设置一个时间间隔,并告之定时器,当到达时间间隔时需要做些什么操作。要达到这样效果,在Java中,需将某个类的对象传递给定时器,然后定时器调用这个对象的方法。定时器需要知道调用哪一个方法,并要求传递的对象所属的类实现了java.awt.event中的ActionListener接口。

    public interface ActionListener{
      void actionPerformed(ActionEvent event);
    }

                  

class TimerPrinter implements ActionListener(){
 public void actionPerformed(ActionEvent event(){
     System.out.println("At the tone the time is "+new Date());
     Toolkit.getDefaultToolkit().beep();
   }
}  
ActionListener listener=new TimerPrinter();
Timer t=new Timer(1000,listner)
t.start();

    2)Comparator接口

                 假设我们希望按长度递增的顺序对字符串进行排序,而不是字典顺序进行排序。肯定不能让String类用两种不同的方式实现compareTo方法。

                 要处理这种情况,Arrays.sort还有第二个版本,有一个数组和一个比较器作为参数。比较器是实现了Comparator接口的类的实例。

   public interface Comparator<T>{
     int compare(T first,T second);
  }
  class LengthComparator implements Comparator<Stirng>{
     public int compare(String first,String second){
       return first.lengt()-second.length();
   }
}

Comparator<String> comp=new LengthComparator();
if(comp.compare(words[i],words[j]){
     ....
}
// 这个compare方法是在比较器对象上调用,而不是在字符串本身上调用

String[] friends={"Peter","Paul","Marry"};
Arrays.sorts(friends,new LengthComparator());

  3)对象克隆

                本节我们会讨论Cloneable接口,这个接口指示了一个类提供了一个安全的clone方法。

                在展开对clone方法的讲解时,我们先回忆下为了一个包含对象引用的变量创建副本会发生什么。

                       原变量和副本都是同一个对象引用。(说明:任何一个变量改变都会影响另一个变量)

   Employee orignal=new Employee("John Public",50000);
   Employee copy=original;

                如果希望copy是一个新对象,它的初始状态与orignal相同,但是之后它们各自会有自己 的不同的状态。这种情况下,只能使用clone()方法。

                 不过并没有那么简单。clone方法是Object类的一个protected方法,说明代码不能直接调用这个方法。只有Employee对象才可以克隆Employee对象。

                      a)如果对象中所有数据域都是数值或其他基本类型,拷贝这些域没有任何问题。

                      b)如果对象包含子对象的引用,拷贝域就会得到相同子对象的另一个引用。这样原对象与克隆的对象仍然会共享一些信息。默认的克隆操作是“浅拷贝”,并没有克隆对象中引用的其他对象。因此,我们通常需要重新定义clone方法来建立一个深拷贝,同时克隆所有的子对象。

                 对于每个类需要确定:

  •  默认的clone是否满足要求(重新定义clone方法,并定义为public访问修饰符)
  • 是否可以在可变的子对象上调用clone来修补默认的clone方法(实现Cloneable接口)
  • 是否不该用clone

                 注意:Cloneable接口的出现就接口的正常使用没有什么关系。它没有指定clone方法。这个接口只是作为一个标记,指示类设计者了解克隆过程。但是,如果对象请求克隆,但没有实现这个接口,就会生成一个受检查异常。

   class Employee implements Cloneable{
      ....
     public Employee clone() throws CloneNotSupportedException{
       return (Employee)super.clone();
   }
}

                 要建立深拷贝的clone方法的一个例子:克隆对象中可变的实例域

  class Employee implements Cloneable{
   ....
 
   public Employee clone throws CloneNotSupportedException{
      Employee cloned=(Employee)super.clone();
      cloned.hireDay=(Date)hireDay.clone();
      return cloned;
   }
}

 

2.lambda表达式

  1)为什么引入lambda表达式

            lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。

            到目前为止,在Java传递一个代码块并不容易,不能直接传递代码块。Java是一种面向对象语言,所以必须构造一个对象,这个对象的类需要有一个方法包含所需的代码。

            因此就引入了lambda表达式来解决这个问题

  2)lambda表达式的语法

            无需指定lambda表达式的返回类型。因为它总能由上下文推导得出。

            a)lambda表达式形式1:参数,箭头(->),以及一个表达式。如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在{}中。即使lambda没有参数,也需要提供空括号

     ①first.length()-second.length();
     (String first,String second)->first.length()-second.length();
     
     ②()->{for(int i=100;i>=0;i--) System.out.println(i);}

   

            b)lambda表达式形式2:如果可以推导一个lambda表达式的参数形式,可以忽略其参数

  Comparator<String>comp=
    (first,second)->first.length()-second.length();

            c)lambda表达式形式3:如果方法只有一个参数,而且这个参数的类型可以推导出,还可以省略小括号

  ActionListener listener=event->System.out.println("The time is "+new Date());

  3)函数式接口

            lambda表达式与很多封装代码块的接口是兼容的。

            函数式接口:对于只有一个抽象方法的接口。需要这种接口的对象时,就可以提供一个lambda表达式。

            最好把lambda表达式看做是一个函数,而不是一个对象。

            a)lambda可以传递到函数式接口

    Arrays.sorts(friends,(first,second)->first.length()-second.length());

            b)lambda表达式可以转换为接口。

   Timer t=new Timer(1000,event->{
      System.out.println("The time is "+new Date());
      Toolkit.getDefaultToolkit().beep();
     }
);

  4)方法引用

  Timer t=new Timer(1000,event->System.out.println(event));

  //  用方法引用来表示
  Timer t=new Timer(1000,System.out::println);
  // System.out::println等价于 x->System.out.println(x)

         要用::操作符分隔方法名与对象或类名,主要有三种情况:

  • object::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod:第一个参数会成为方法的目标

         可在方法引用中使用this参数,this::equals相当于x->this.equals(x)

         使用super也是合法的。super::instanceMethod

  5)构造器引用

            构造器引用于方法引用很类似,只不过方法名为new。

            可以用数组类型建立构造器引用。int[]::new是一个构造器引用,它有一个参数是数组的长度,相当于x->new int[n]; 

  6)变量作用域

           lambda表达式有三部分:一个代码块、参数和自由变量的值。

           表示lambda表达式的数据结构必须存储自由变量的值。

           lambda表达式可以捕获外围作用域中变量的值,在这里有个重要的限制:lambda表达式中,只能引用值不会改变的变量(effectively final)。

           lambda表达式的体与嵌套块有相同的作用域。这里同样适用于命名冲突和遮蔽的有关规定。

  7)处理lambda表达式

            处理lambda表达式的重点就是 延迟执行。 

  8)再谈Comparator

           Comparator接口包含很多方便的静态方法来创建比较器。这些方法也适用于lambda表达式或方法引用

           例如:

    Arrays.sort(people,Comparator.comparing(Person::getName);

    Arrays.sort(people,Comparator.comparing(Person::getLastName).
     thenComparing(Person::getFirstName));

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值