JAVA学习笔记(5)接口 , lambda 表达式,内部类的理解

接口(interface)

接口概念

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

Arrays 类中的 sort 方法承诺可以对对象数组进行排序, 但要求满足下列前提 : 对象所属的类必须实现了 Comparable 接口 ,因为sort排序的时候需要用到接口中的函数compareTo作比较。

下面是 Comparable 接口的代码 :

public interface Comparable<T> {
    
    
    public int compareTo(T o);
}

这就是说 , 任何实现 Comparable 接口的类都需要包含 compareTo 方法, 并且这个方法的参数必须是一个 Object 对象 , 返回一个整型数值 。

接口中的所有方法自动地属于 public 。 因此, 在接口中声明方法时 , 不必提供关键字public。抽象类中的抽象方法默认和普通的类相同。和interface不同。

当然, 接口中还有一个没有明确说明的附加要求 : 在调用 x.compareTo(y) 的时候 , 这个compareTo方法必须确实比较两个对象的内容 , 并返回比较的结果 。 当 x 小于 y 时, 返回一个负数 ; 当 x 等于 y 时 , 返回 0 ; 否则返回一个正数 。

案例:

现在 , 假设希望使用 Arrays 类的 sort 方法对 Employee 对象数组进行排序 , Employee 类就必须实现 Comparable 接口 。

public class Employee implements Comparable<Employee>
{
    
    
   private String name;
   private double salary;

   public Employee(String name, double salary)
   {
    
    
      this.name = name;
      this.salary = salary;
   }

   public String getName()
   {
    
    
      return name;
   }

   public double getSalary()
   {
    
    
      return salary;
   }

   public void raiseSalary(double byPercent)
   {
    
    
      double raise = salary * byPercent / 100;
      salary += raise;
   }
   
   public int compareTo(Employee other)
   {
    
    
      return Double.compare(salary, other.salary);
   }
}

接口特性

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

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

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

Comparable x ; // OK

接口变量必须引用实现了接口的类对象 :

x = new Employee ( .. . ) ; //

接下来, 如同使用 instanceof 检查一个对象是否属于某个特定类一样, 也可以使用instance 检查一个对象是否实现了某个特的接口 :

if ( anObject instanceof Comparable) {
    
    . . . }

与可以建立类的继承关系一样, 接口也可以被扩展 。 例如, 假设有一个称为 Moveable 的接口 ,可以以它为基础扩展一个叫做 Powered 的接口 :

public interface Moveable
{
    
    
void move (double x , double y ) ;
}

public interface Powered extends Moveable
{
    
    
double milesPerCallon() ;
}

接口可以有常量,但是接口中的域将被自动设为 public static final ,接口中的方法都自动地被设置为 public

接口与抽象类

为什么不将 Comparable 直接设计成如下所示的抽象类 。

abstract class Comparable // why not?
{
    
    
	public abstract int compareTo(Object other) ;
}

然后 , Employee 类再直接扩展这个抽象类, 并提供 compareTo 方法的实现 :

class Employee extends Comparable // why not ?
{
    
    
	public int compareTo(Object other) {
    
     . . . }
}

使用抽象类表示通用属性存在这样一个问题 : 每个类只能扩展于一个类 。 假设 Employee 类已经扩展于一个类, 例如 Person , 它就不能再像下面这样扩展第二个类了 :

class Employee extends Person , Comparable 

但每个类可以像下面这样实现多个接口 :

cl ass Employee extends Person implements Comparable // OK

静态方法

在 Java SE 8 中, 允许在接口中增加静态方法 。 理论上讲 , 没有任何理由认为这是不合法的 。 只是这有违于将接口作为抽象规范的初衷 。**
public class EmployeeSortTest
{
public static void main(String[] args)
{
System.out.println(A.returnId());
}
}
interface A{
public static int returnId(){
return 1;
}
}

目前为止, 通常的做法都是将静态方法放在伴随类中 。 在标准库中 , 你会看到成对出现的接口 和实用工具类 , 如 Collection/ Collections 或 Path / Paths 。**

默认方法

可以为接口方法提供一个默认实现 。 必须用 default 修饰符标记这样一个方法

public interface Comparable<T>			// 实际标准包中没有这个类
{
    
    
	default int compareTo ( T other ) {
    
     return 0; }
	// By default , all elements are the same
}

当然 , 这并没有太大用处 , 因为 Comparable 的每一个实际实现都要覆盖这个方法 。 不过有些情况下 , 默认方法可能很用。

解决默认方法冲突

如果先在一个接口中将一个方法定义为默认方法 , 然后又在超类或另一个接口中定义了同样的方法, 会发生什么情况 ? 诸如 Scala 和 C + + 等语言对于解决这种二义性有一些复杂的规则 。 幸运的是, Java 的相应规则要简单得多 。 规则如下 :

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

案例:

interface A{
    
    
   default public int returnId(){
    
    return 9;};
}

interface B{
    
    
   public int returnId();
}

现在来考虑另一种情况 , 一个类扩展了一个超类 ,同时实现了一个接口 , 并从超类和接口继承了相同的方法 。

interface A{
    
    
   default public int returnId(){
    
    return 9;}
}

interface B{
    
    
   public int returnId();
}

class D{
    
    
   public int returnId(){
    
    return 9;}
}

class C extends D implements B{
    
    

}

接口示例

Comparator 接口

现在假设我们希望按长度递增的顺序对字符串进行排序, 而不是按字典顺序进行排序 。

要处理这种情况 , Arrays. Sort 方法还有第二个版本, 有一个数组和一个比较器 ( comparator )

Arrays.sort (friends , new LengthComparator() );

例如:

对象克隆

要了解克隆的具体含义, 先来回忆为一个包含对象引用的变量建立副本时会发生什么 。原变量和副本都是同一个对象的引用。

Employee original = new Employee ( "John Public" , 50000 ) ;
Employee copy = original ;
copy raiseSalary ( 10 ) ; // oops - also changed original

在这里插入图片描述

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

Employee copy = original , cl one () ;
copy raiseSalary ( 10 ) ; / / OK original unchanged

不过并没有这么简单 。 clone 方法是 Object 的一个 protected 方法 , 这说明你的代码不能直接调用这个方法 。 只有Employee 类可以克隆 Employee 对象 。 这个限制是有原因的 。 想想看 Object 类如何实现 clone 。 它对于这个对象一无所知 , 所以只能逐个域地进行拷贝 。 如果对象中的所有数据域都是数值或其他基本类型, 拷贝这些域没有任何问题 、 但是如果对象包含子对象的引用, 拷贝域就会得到相同子对象的另一个引用, 这样一来, 原对象和克隆的对象仍然会共享一些信息 。

在这里插入图片描述

浅拷贝会有什么影响吗 ? 这要看具体情况 。 如果原对象和浅克隆对象共享的子对象是不可变的 , 那么这种共享就是安全的 。 如果子对象属于一个不可变的类, 如 String , 就 是 这 种情况 。 或者在对象的生命期中, 子对象一直包含不变的常量 , 没有更改器方法会改变它, 也没有方法会生成它的引用, 这种情况下同样是安全的 。

不过 , 通常子对象都是可变的, 必须重新定义 clone 方法来建立一个深拷贝 , 同时克隆所有子对象 。 在这个例子中, hireDay 域是一个 Date , 这是可变的 , 所以它也需要克隆 。 (出于这个原因 , 这个例子使用 Date 类型的域而不是 LocalDate 来展示克隆过程 。 如果 hireDay是不可变的 LocalDate 类的一个实例, 就无需我们做任何处理了 。)

对于每一个类, 需要确定 :
1 ) 默 认 的 clone 方法是否满足要求 ;
2 ) 是否可以在可变的子对象上调用 clone 来修补默认的 clone 方法 ;
3 ) 是否不该使用 clone

实际上第 3 个选项是默认选项 。 如果选择第 1 项或第 2 项, 类必须 :
1 ) 实现 Cloneable 接口 ;
2 ) 重新定义 clone 方法, 并指定 public 访问修饰符 。

lambda 表达式

lambda 表达式采用一种简洁的语法定义代码块, 以及如何编写处理 lambda 表达式的代码 。

lambda 表达式

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

如果想按长度而不是默认的字典顺序对字符串排序, 可以向 sort 方法传人一个 Comparator 对象 :如果想按长度而不是默认的字典顺序字符串排序, 可以向 sort 方法传人一个 Comparator 对象 :

class LengthTest implements Comparator<String>{
    
    
   @Override
   public int compare(String o1, String o2) {
    
    
    return o1.length() - o2.length();
   }
}

compare 方法不是立即调用 。 实际上 , 在数组完成排序之前, sort 方法会一直调用compare 方法, 只要元素的顺序不正确就会重新排列元素, 将比较元素所需的代码段放在sort 方法中 , 这个代码将与其余的排序逻辑集成

这两个例子有一些共同点 , 都是将一个代码块传递到某个对象 ,这个代码块会在将来某个时间调用 。

到目前为止, 在 Java 中传递一个代码段并不容易, 不能直接传递代码段 _ Java 是一种面向对象语言, 所以必须构造一个对象 , 这个对象的类需要有一个方法能包含所需的代码**.(学到这里,我发现这个东西类似于函数指针)**

lambda 表达式的语法

first 和 second 是什么 ? 它们都是字符串 。 Java 是一种强类型语言, 所以我们还要指定它们的类型 :

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

Java 中的一种 lambda 表达式形式 : 参数, 箭头 ( -> ) 以及一个表达式 。 如果代码要完成的计算无法放在一个表达式中 , 就可以像写方法一样, 把这些代码放在 {}中,并包含显式的 return 语句 。 例如 :

(String first, String second) ->
{
    
    
	if ( first . length() < second . length() )   return -1 ;
	else if ( first . length() > second . length() )  return 1;
	else return 0;
}

即使 lambda 表达式没有参数 , 仍然要提供空括号, 就像无参数方法一样 :

( ) - > {
    
     for ( int i = 100; i > = 0; i --)  System out println ( i ) ; }

无需指定 lambda 表达式的返回类型 。 lambda 表达式的返回类型总是会由上下文推导得出 。 例如, 下面的表达式:

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

可以在需要 int 类型结果的上下文中使用 。(我的理解是 first length() - second.length()会自动识别为int类型)

如果一个 lambda 表达式只在某些分支返回一个值, 而在另外一些分支不返回值,这是不合法的 。 例如 , ( int x ) - > { if (x >= 0 ) return 1 ; } 就不合法 。

如果方法只有一个参数 , 而且这个参数的类型可以推导得出 , 那么甚至还可以省略小括号 :

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

案例代码:

import java.util.*;


/**
 * This program demonstrates the use of lambda expressions.
 * @version 1.0 2015-05-12
 * @author Cay Horstmann
 */
public class LambdaTest
{
    
    
   public static void main(String[] args)
   {
    
    
      String[] planets = new String[] {
    
     "Mercury", "Venus", "Earth", "Mars", 
            "Jupiter", "Saturn", "Uranus", "Neptune" };
      System.out.println("Sorted by length:");
      Arrays.sort(planets , new LengthTest());
      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());	// 运用lambda表达式
      System.out.println(Arrays.toString(planets));      
   }
}

class LengthTest implements Comparator<String>{
    
    
   @Override
   public int compare(String o1, String o2) {
    
    
    return o1.length() - o2.length();
   }
}

函数式接口

lambda 表达式与这些接口是兼容的 ,有很多封装代码块的接口Comparator

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

Java8中的函数式接口定义是只有一个抽象方法的接口,但是这个Comparator中,好像是有多个方法。但是实际上只有一个。int compare(T o1,T o2)defualt方法 默认方法有点类似静态方法。equals方法是Object的方法,

所以可以提供一个 lambda 表达式 :

Arrays.sort(planets, (first, second) -> first.length() - second.length());

在底层 , Arrays . sort 方法会接收实现了 Comparator 的某个类的对象 。 在这个对象上调用 compare 方法会执行这个 lambda 表达式的体 。 这些对象和类的管理完全取决于具体实现, 与使用传统的内联类相比, 这样可能要高效得多 。 最好把 lambda 表达式看作是一
个函数 , 而不是一个对象 , 另外要接受 lambda 表达式可以传递到函数式接口 。

方法引用

有时 , 可能已经有现成的方法可以完成你想要传递到其他代码的某个动作 。

假设你想对字符串排序, 而不考虑字母的大小写 。 可以传递以下方法表达式 :

Arrays.sort(planets , String::compareToIgnoreCase);

从这些例子可以看出 , 要用: : 操作符分隔方法名与对象或类名 。 主要有 3 种情况 :

object :: instanceMethod
Class::staticMethod
Class :: instanceMethod

在前 2 种情况中 , 方法引用等价于提供方法参数的 lambda 表达式 。 类似地 , Math :: pow 等价于( x,y) ->
System. out:: println 等价于 x - > System. out. println( x)

对于第 3 种情况 , 第 1 个参数会成为方法的目标 。 例如 , String :: compareToIgnoreCase 等同于 (x, y ) - > x. compareToIgnoreCase( y ).
这一点我的理解是:类不能直接调 实例方法,所以需要一个相应的对象调用方法,这个方法有一个参数,自然就是lambda表达式中的(x ,y ),内容自然就是x.方法(y)

lambada后续内容遇到再补上…

内部类

内部类 ( inner class ) 是定义在另一个类中的类 。 为什么需要使用内部类呢 ?

其主要原因有以下三点 :

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

使用内部类访问对象状态

内部类的语法比较复杂 。 鉴于此情况, 我们选择一个简单但不太实用的例子说明内部类的使用方式 。 下面将进一步分析 TimerTest 示例, 并抽象出一个 TalkingClock 类 。 构造一个语音时钟时需要提供两个参数 : 发布通告的间隔和开关铃声的标志 。

package innerClass;

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

/**
 * This program demonstrates the use of inner classes.
 * @version 1.11 2015-05-12
 * @author Cay Horstmann
 */
public class InnerClassTest
{
    
    
   public static void main(String[] args)
   {
    
    
      TalkingClock clock = new TalkingClock(1000, true);
      clock.start();

      // keep program running until user selects "Ok"
      JOptionPane.showMessageDialog(null, "Quit program?");
      System.exit(0);
   }
}

/**
 * A clock that prints the time in regular intervals.
 */
class TalkingClock
{
    
    
   private int interval;
   private boolean beep;

   /**
    * Constructs a talking clock
    * @param interval the interval between messages (in milliseconds)
    * @param beep true if the clock should beep
    */
   public TalkingClock(int interval, boolean beep)
   {
    
    
      this.interval = interval;
      this.beep = beep;
   }

   /**
    * Starts the clock.
    */
   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();
      }
   }
}

在这里插入图片描述
令人惊讶的事情发生了 。 TimePrinter 类没有实例域或者名为 beep 的变量 , 取而代之的是beep 引用了创建 TimePrinter 的 TalkingClock 对象的域 。 这是一种创新的想法 。 从传统意义上讲, 一个方法可以引用调用这个方法的对象数据域。 内部类既可以访问自身的数据域, 也可以访问创建它的外围类对象的数据域 .

这个引用在内部类的定义中是不可见的 。 然而, 为了说明这个概念 , 我们将外围类对象的引用称为 outer 。 于是 actionPerformed 方法将等价于下列形式 :

public void actionPerformed ( ActionEvent event )
{
    
    
	System out println("At the tone , the time is " + new DateO ) ;
	if (outer.beep) 
		Toolkit getDefaultToolkit() beep() ;
}

内部类的特殊语法规则

使用外围类引用的正规语法还要复杂一些 。 表达式

OuterClass.this		// 表示外围类引用 

可以像下面这样编写 TimePrinter 内部类的 actionPerformed 方法 :

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

如果如果 TimePrinter 是一个公有内部类 , 对于任意的语音时钟都可以构造一个 TimePrinter :

	TalkingClock jj = new TalkingClock(1000 , true);
    TalkingClock.TimePrinter listener = jj.new TimePrinter();

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

OuterClass.InnerClass

内部类中声明的所有静态域都必须是 final 。 原因很简单 。 我们希望一个静态域只有一个实例, 不过对于每个外部对象 , 会分别有一个单独的内部类实例 。 如果这个域不是 final , 它可能就不是唯一的 。

理解:
把一个类声明为内部类,通常是因为它和外围类是有一定联系的。现在我们把手机声明为外围类,手机屏幕声明为内部类

public class MobilePhone {
    
    
      private String name;
      
      public void create(Screen screen) {
    
    
		...
		// 通过给定的手机屏做了一个手机
		...
      }
      
      // 内部类,只有内部类可以声明为私有
	  public class Screen {
    
    
	  //屏幕大小
	  public static final double size = 5.6;
     }
}

现在张三和李四都创建了一个手机。现在假设张三嫌屏幕太小。于是想通过MobilePhone.Screen.size = 10;修改下屏幕的大小。我们知道类的static属性是共享的,那么可想而知李四的屏幕也跟着“膨胀”了。这样会吓到别人滴。为了避免这种风险,对于“共享变量(static)”,一定要final。

内部类的形式

局部内部类

如果仔细地阅读一下 TalkingClock 示例的代码就会发现 , TimePrinter 这个类名字只在start 方法中创建这个类型的对象时使用了一次 。

当遇到这类情况时, 可以在一个方法中定义局部类 。

public void start(int interval, boolean beep)
   {
    
    
      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();
   }

局部类不能用 public 或 private 访问说明符进行声明 。 它的作用域被限定在声明这个局部类的块中 。局部类有一个优势, 即对外部世界可以完全地隐藏起来 。 即使 TalkingClock 类中的其他代码也不能访问它 。 除 start 方法之外 , 没有任何方法知道 TimePrinter 类的存在 。

与其他内部类相比较 , 局部类还有一个优点 。 它们不仅能够访问包含它们的外部类, 还可以访问局部变量 。 不过 , 那些局部变量必须事实上为 final 。 这说明, 它们一旦赋值就绝不会改变 。

package localInnerClass;

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

/**
 * This program demonstrates the use of local inner classes.
 * @version 1.01 2015-05-12
 * @author Cay Horstmann
 */
public class LocalInnerClassTest
{
    
    
   public static void main(String[] args)
   {
    
    
      TalkingClock clock = new TalkingClock(111);
      clock.start(1000, true);

      // keep program running until user selects "Ok"
      JOptionPane.showMessageDialog(null, "Quit program?");
      System.exit(0);
   }
}

/**
 * A clock that prints the time in regular intervals.
 */
class TalkingClock
{
    
    
   private int beep ;
   public TalkingClock(int i){
    
    
      this.beep = i;
   }
   /**
    * Starts the clock.
    * @param interval the interval between messages (in milliseconds)
    * @param beep true if the clock should beep
    */
   public void start(int interval, boolean beep)
   {
    
    
      class TimePrinter implements ActionListener
      {
    
    
         public void actionPerformed(ActionEvent event)
         {
    
    
            System.out.println("At the tone, the time is " + new Date());
            System.out.println(TalkingClock.this.beep);
            System.out.println(beep);
            if (beep) Toolkit.getDefaultToolkit().beep();
         }
      }
      ActionListener listener = new TimePrinter();
      Timer t = new Timer(interval, listener);
      t.start();
   }
}

匿名内部类

将局部内部类的使用再深人一步 。 假如只创建这个类的一个对象, 就不必命名了 。 这种类被称为匿名内部类 ( anonymous inner class ) 。

先说一下什么时候使用匿名内部类,即使用前提和条件:必须存在继承和实现关系的时候才可以使用,其实这也很好理解,首先,匿名内类没有名字,那该如何描述以及new个对象呢?对,没错,要通过继承它的父类或者实现一个接口来达成这一目的。

我的理解:

实现接口:

class TalkingClock
{
    
    
   /**
    * Starts the clock.
    * @param interval the interval between messages (in milliseconds)
    * @param beep true if the clock should beep
    */
   public void start(int interval, boolean beep)
   {
    
    
      ActionListener listener = new ActionListener()		// listener是一个引用,指向了实现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();
   }
}

继承夫类:

public class AnimalTest {
    
    

    private final String ANIMAL = "动物";

    public void accessTest() {
    
    
        System.out.println("匿名内部类访问其外部类方法");
    }

    class Animal {
    
    
        private String name;

        public Animal(String name) {
    
    
            this.name = name;
        }

        public void printAnimalName() {
    
    
            System.out.println(bird.name);
        }
    }

    // 这个bird引用指向:一个Animal的匿名子类,覆盖了父类的方法,并且这种匿名类不能有构造函数
    Animal bird = new Animal("布谷鸟") {
    
    

        @Override
        public void printAnimalName() {
    
    
            accessTest();           // 访问外部类成员
            System.out.println(ANIMAL);  // 访问外部类final修饰的变量
            super.printAnimalName();
        }
    };

    public void print() {
    
    
        bird.printAnimalName();
    }

    public static void main(String[] args) {
    
    

        AnimalTest animalTest = new AnimalTest();
        animalTest.print();
    }
}

静态内部类

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

我的理解是:静态内部类不需要访问实例对象,可以访问静态变量,可以把这种内部类看成是外部类的一个属性。

package staticInnerClass;

/**
 * This program demonstrates the use of static inner classes.
 * @version 1.02 2015-05-12
 * @author Cay Horstmann
 */
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());
      ArrayAlg.Pair a  = new ArrayAlg.Pair(1,2);
   }
}

class ArrayAlg
{
    
    
   /**
    * A pair of floating-point numbers
    */
   public static class Pair
   {
    
    
      private double first;
      private double second;

      /**
       * Constructs a pair from two floating-point numbers
       * @param f the first number
       * @param s the second number
       */
      public Pair(double f, double s)
      {
    
    
         first = f;
         second = s;
      }

      /**
       * Returns the first number of the pair
       * @return the first number
       */
      public double getFirst()
      {
    
    
         return first;
      }

      /**
       * Returns the second number of the pair
       * @return the second number
       */
      public double getSecond()
      {
    
    
         return second;
      }
   }

   /**
    * Computes both the minimum and the maximum of an array
    * @param values an array of floating-point numbers
    * @return a pair whose first element is the minimum and whose second element
    * is the maximum
    */
   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);
   }
}

猜你喜欢

转载自blog.csdn.net/liutao43/article/details/109791630