第6章 接口、lambda表达式与内部类

第6章 接口、lambda表达式与内部类

  • 接口(interface)技术:这种技术主要用来描述类具有什么功能,而并不给出每个功能的具体实现。一个类可以实现(implement)一个或多个接口,并在需要接口的地方,随时使用实现了相应接口的对象
  • lambda表达式:这是一种表示可以在将来某个时间点执行的代码块的简洁方法。使用lambda表达式,可以用一种精巧而简洁的方式表示使用回调或变量行为的代码
  • 内部类(inner class)机制:内部类(innerclass)机制。理论上讲,内部类有些复杂,内部类定义在另外一个类的内部,其中的方法可以访问包含它们的外部类的域。内部类技术主要用于设计具有相互协作关系的类集合
  • 代理(proxy): 这是一种实现任意借口的对象,代理是一种非常专业的构造工具,可以用来构建系统级的工具

6.1 接口

6.1.1 接口概念

Java程序设计语言中,接口不是类,而是对类的一组需求描述,这些类要遵从接口描述的统一格式进行定义

接口中的所有方法自动地属于public。因此,在接口中声明方法时,不必提供关键字public

public interface Comparable
{
    int compareTo(Object other);
}
  • 接口可以定义常量
  • 接口绝不能含有实例域
  • 接口不能引用实例域

为了让类实现一个接口,通常需要下面两个步骤:

  1. 将类声明为实现给定的接口
  2. 对接口中的所有方法进行定义
class Employee implements Comparable{
	// ...
    public int comparaTo(Object otherObject)
    {
        Employee other = (Employee) otherObject;
        return Double.compare(salary, other.salary);
    }
}

在这里, 我们使用了静态 Double.compare 方法 , 如果第一个参数小于第二个参数 , 它会返回一个负值 ; 如果二者相等则返回 0; 否则返回一个正值

对泛型Comparable接口提供一个类型参数

class Employee implements Comparable<Employee>
{
	public int compareTo(Employee other)
    {
        return Double.compare(salary, other.salary);
    }
}

Java 程序设计语言是一种强类型 ( strongly typed ) 语言 。 在调用方法的时
候 , 编译器将会检查这个方法是否存在

6.1.2 接口的特性

  • 接口不是类, 尤其不能使用 new 运算符实例化一个接口

    x = new Comparable(...); //ERROR

  • 然而,尽管不嫩构造接口的对象,却能声明接口的变量

    Compare x; //OK

  • 接口变量必须应用实现了接口的类对象

    x = new Employee(...); // OK provided Employee implements Comparable

  • 可以使用instance检测一个对象是否实现了某个特定的接口

    if(anObject instanceof Comparable) {...}

6.1.3 接口与抽象类

Java不支持多继承,所以引入了接口,每个类可以实现多个接口

class Employee extends Person implements Comparable, Cloneable{...}

6.1.6 解决默认方法冲突

  • 超类优先 如果超类提供了一个具体方法,同名而且有相同参数类型的默认方法会被忽略
  • 接口冲突 如果一个超接口提供了一个默认方法,另一个接口提供了一个同名而且参数类型(不论是否是默认参数)相同的方法,必须覆盖这个方法来解决冲突

6.2 接口示例

6.2.1 接口与回调

**回调(callback)**是一种常见的程序设计模式,在这种模式中,可以指出某个特定时间发送时应该采取的动作。

利用java.awt.eventActionListener接口实现了10秒钟打印一条数据,并响一声

package chap6.timer;

import java.awt.Toolkit;
import javax.swing.JOptionPane;
import javax.swing.Timer;
import java.awt.event.*;
import java.util.*;

public class TimerTest {
    public static void main(String[] args)
    {
        ActionListener listener = new TimePrinter();

        Timer t = new Timer(10000, listener);
        t.start();
        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);
    }
}

class TimePrinter implements ActionListener
{
    public void actionPerformed(ActionEvent event)
    {
        System.out.println("At the tone, the time is " + new Date());
        Toolkit.getDefaultToolkit().beep();
    }
}

6.2.3 对象克隆

拷贝即原变量和副本都是同一个对象的引用,任何一个变量改变都会影响另一个变量

Employee original = new Employee("John Public", 50000);
Employee copy = original;
copy.raiseSalary(10);

如果是想要新的对象初始状态和原变量相同,但是之后改变的状态不同,这时候就需要使用clone方法

Employee copy = original.clone();
copy.raiseSalary(10);

Object类实现clone是逐个域拷贝,如果对象包含子对象的引用,拷贝域就会得到相同子对象的另一个引用,这样一来,原对象和克隆的对象仍然会共享一些信息

例如Employee类的hireDayDate,这是可变的,所以它也需要克隆

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

6.3 lambda表达式

6.3.2 lambda表达式的语法

(String first, String second)
	-> first.length() - second.length()

chap6/interface/lambda/LambdaTest.java

package chap6.lambda;

import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

public class LambdaTest {
    public static void main(String[] args)
    {
        String[] planets = new String[] {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Sature", "Uranus", "Neptune"};
        System.out.println(Arrays.toString(planets));
        System.out.println("Sorted in dictionary order: ");
        Arrays.sort(planets);
        System.out.println(Arrays.toString(planets));
        System.out.println("Sorted by length: ");
        Arrays.sort(planets, (first, second) -> first.length() - second.length());
        System.out.println(Arrays.toString(planets));

        Timer t = new Timer(1000, event -> System.out.println("The time is " + new Date()));
        t.start();

        JOptionPane.showMessageDialog(null, "Quit program>");
        System.exit(0);
    }
}

6.4

内部类(innerclass)是定义在另一个类中的类,使用内部类的原因:

  • 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据
  • 内部类可以对同一个包中的其他类隐藏起来
  • 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷

6.4.1 使用内部类访问对象状态

内部类既可以访问自身的数据域,也可以访问创建它的外围类的数据域

chap6/innerClass/InnerClassTest.java

package chap6.innerClass;

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

public class InnerClassTest {
    public static void main(String[] args){
        TalkingClock clock = new TalkingClock(1000, true);
        clock.start();

        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);
    }
}

class TalkingClock
{
    private int interval;
    private boolean beep;

    public TalkingClock(int interval, boolean beep)
    {
        this.interval = interval;
        this.beep = beep;
    }

    public void start()
    {
        ActionListener listener = new TimePrinter();
        Timer t = new Timer(interval, listener);
        t.start();
    }

    public class TimePrinter implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {
            System.out.println("At the tone, the time is " + new Date());
            if (beep) Toolkit.getDefaultToolkit().beep();
        }
    }
}

6.4.2 内部类的特殊语法规则

内部类引用外围类的正规语法

OuterClass.this

可以使用下面的方式编写TimerPrinter内部类的actionPerformed方法

public void actionPerformed(ActionEvent event)
{
    // ...
    if(TalkingClock.this.beep) Toolkit.getDefaultToolKit().beep();
}

反过来,也可以使用下列盘语法编写内部对象的构造器

outerObject.new InnerClass(construction parameters)

例如

ActionListener listener = this.new TimePrinter();

在外围类的作用域之外,可以这样引用内部类:

OuterClass.InnerClass

6.4.4 局部内部类

上述的代码TimerPrinter这个类名字只在start方法中创建这个类型对象时使用了一次,当遇到这类情况时,可以在一个方法中定义局部类

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

局部类不能使用publicprivate访问说明符进行声明,它的作用域被限定在声明这个局部类的块中

6.4.6 匿名内部类

public vodi start(int interval, boolean beep)
{
    ActionListener listener = new ActionListener()
    {
        public void actionPerformed(ActionEvent event)
        {
            System.out.println("At the tone, the time is " + new Date());
            if(beep) Toolkit.getDefaultToolkit().beep();
        }
    }
    Timer t = new Timer(interval, listener);
    t.start();
}

6.4.7 静态内部类

有时候,使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要内部类引用外围类对象。为此,可以将内部类声明为static,以便取消产生的引用

例如以下代码

double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (double v : values)
{
    if(min > v) min = v;
    if(max < v) max = v;
}

staticInnerClass/StaticInnerClassTest.java

package chap6.staticInnerClass;

public class StaticInnerClassTest {
    public static void main(String[] args)
    {
        double[] d = new double[20];
        for(int i = 0; i < d.length; i++)
            d[i] = 100 * Math.random();
        ArrayAlg.Pair p = ArrayAlg.minmax(d);
        System.out.println("min = " + p.getFirst());
        System.out.println("max = " + p.getSecond());
    }
}

class ArrayAlg
{
    public static class Pair
    {
        private double first;
        private double second;

        public Pair(double f, double s)
        {
            first = f;
            second = s;
        }

        public double getFirst()
        {
            return first;
        }

        public double getSecond(){
            return second;
        }
    }

    public static Pair minmax(double[] values)
    {
        double min = Double.POSITIVE_INFINITY;
        double max = Double.NEGATIVE_INFINITY;
        for(double v : values)
        {
            if(min > v) min = v;
            if(max < v) max = v;
        }
        return new Pair(min, max);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值