The Java Tutorials have been written for JDK 8.Java教程是为JDK 8编写的。Examples and practices described in this page don't take advantage of improvements introduced in later releases and might use technology no longer available.本页中描述的示例和实践没有利用后续版本中引入的改进,并且可能使用不再可用的技术。See Java Language Changes for a summary of updated language features in Java SE 9 and subsequent releases.有关Java SE 9及其后续版本中更新的语言特性的摘要,请参阅Java语言更改。
See JDK Release Notes for information about new features, enhancements, and removed or deprecated options for all JDK releases.有关所有JDK版本的新功能、增强功能以及已删除或不推荐的选项的信息,请参阅JDK发行说明。
The section Interfaces describes an example that involves manufacturers of computer-controlled cars who publish industry-standard interfaces that describe which methods can be invoked to operate their cars. 接口部分描述了一个示例,涉及计算机控制汽车制造商,他们发布了行业标准接口,描述了可以调用哪些方法来操作汽车。What if those computer-controlled car manufacturers add new functionality, such as flight, to their cars? 如果这些计算机控制的汽车制造商在他们的汽车上增加了新的功能,比如飞行,会怎么样?These manufacturers would need to specify new methods to enable other companies (such as electronic guidance instrument manufacturers) to adapt their software to flying cars. 这些制造商需要指定新的方法,以使其他公司(如电子制导仪器制造商)能够将其软件适应飞行汽车。Where would these car manufacturers declare these new flight-related methods? 这些汽车制造商将在哪里宣布这些新的飞行相关方法?If they add them to their original interfaces, then programmers who have implemented those interfaces would have to rewrite their implementations. 如果他们将它们添加到原始接口中,那么实现这些接口的程序员将不得不重写它们的实现。If they add them as static methods, then programmers would regard them as utility methods, not as essential, core methods.如果将它们添加为静态方法,那么程序员会将它们视为实用方法,而不是基本的核心方法。
Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces.默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。
Consider the following interface, 考虑下面的接口,TimeClient, as described in Answers to Questions and Exercises: Interfaces:TimeClient,如在“问题和练习:接口”的答案中所描述的:
import java.time.*;
public interface TimeClient {
void setTime(int hour, int minute, int second);
void setDate(int day, int month, int year);
void setDateAndTime(int day, int month, int year,
int hour, int minute, int second);
LocalDateTime getLocalDateTime();
}The following class, 以下类SimpleTimeClient, implements TimeClient:SimpleTimeClient实现了TimeClient接口:
package defaultmethods;
import java.time.*;
import java.lang.*;
import java.util.*;
public class SimpleTimeClient implements TimeClient {
private LocalDateTime dateAndTime;
public SimpleTimeClient() {
dateAndTime = LocalDateTime.now();
}
public void setTime(int hour, int minute, int second) {
LocalDate currentDate = LocalDate.from(dateAndTime);
LocalTime timeToSet = LocalTime.of(hour, minute, second);
dateAndTime = LocalDateTime.of(currentDate, timeToSet);
}
public void setDate(int day, int month, int year) {
LocalDate dateToSet = LocalDate.of(day, month, year);
LocalTime currentTime = LocalTime.from(dateAndTime);
dateAndTime = LocalDateTime.of(dateToSet, currentTime);
}
public void setDateAndTime(int day, int month, int year,
int hour, int minute, int second) {
LocalDate dateToSet = LocalDate.of(day, month, year);
LocalTime timeToSet = LocalTime.of(hour, minute, second);
dateAndTime = LocalDateTime.of(dateToSet, timeToSet);
}
public LocalDateTime getLocalDateTime() {
return dateAndTime;
}
public String toString() {
return dateAndTime.toString();
}
public static void main(String... args) {
TimeClient myTimeClient = new SimpleTimeClient();
System.out.println(myTimeClient.toString());
}
}Suppose that you want to add new functionality to the 假设您希望向TimeClient interface, such as the ability to specify a time zone through a ZonedDateTime object (which is like a LocalDateTime object except that it stores time zone information):TimeClient接口添加新功能,例如通过ZonedDateTime对象(与LocalDateTime对象类似,只是它还存储时区信息)指定时区的功能:
public interface TimeClient {
void setTime(int hour, int minute, int second);
void setDate(int day, int month, int year);
void setDateAndTime(int day, int month, int year,
int hour, int minute, int second);
LocalDateTime getLocalDateTime();
ZonedDateTime getZonedDateTime(String zoneString);
}Following this modification to the 修改TimeClient interface, you would also have to modify the class SimpleTimeClient and implement the method getZonedDateTime. TimeClient接口之后,还必须修改SimpleTimeClient类并实现getZonedDateTime方法。However, rather than leaving 但是,您可以定义一个默认实现,而不是将getZonedDateTime as abstract (as in the previous example), you can instead define a default implementation. getZonedDateTime保留为抽象的(如前一个示例中所示)。(Remember that an abstract method is a method declared without an implementation.)(请记住,抽象方法是在没有实现的情况下声明的方法。)
package defaultmethods;
import java.time.*;
public interface TimeClient {
void setTime(int hour, int minute, int second);
void setDate(int day, int month, int year);
void setDateAndTime(int day, int month, int year,
int hour, int minute, int second);
LocalDateTime getLocalDateTime();
static ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString +
"; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}You specify that a method definition in an interface is a default method with the 指定接口中的方法定义为默认方法,方法签名开头带有default keyword at the beginning of the method signature. default关键字。All method declarations in an interface, including default methods, are implicitly 接口中的所有方法声明(包括默认方法)都是隐式公开的,因此可以省略public, so you can omit the public modifier.public修饰符。
With this interface, you do not have to modify the class 使用此接口,您不必修改SimpleTimeClient, and this class (and any class that implements the interface TimeClient), will have the method getZonedDateTime already defined. SimpleTimeClient类,并且该类(以及实现接口TimeClient的任何类)将具有已定义的getZonedDateTime方法。The following example, 以下示例TestSimpleTimeClient, invokes the method getZonedDateTime from an instance of SimpleTimeClient:TestSimpleTimeClient从SimpleTimeClient实例调用getZonedDateTime方法:
package defaultmethods;
import java.time.*;
import java.lang.*;
import java.util.*;
public class TestSimpleTimeClient {
public static void main(String... args) {
TimeClient myTimeClient = new SimpleTimeClient();
System.out.println("Current time: " + myTimeClient.toString());
System.out.println("Time in California: " +
myTimeClient.getZonedDateTime("Blah blah").toString());
}
}When you extend an interface that contains a default method, you can do the following:扩展包含默认方法的接口时,可以执行以下操作:
abstract.abstract。Suppose that you extend the interface 假设您按如下方式扩展接口TimeClient as follows:TimeClient:
public interface AnotherTimeClient extends TimeClient { }Any class that implements the interface 任何实现AnotherTimeClient will have the implementation specified by the default method TimeClient.getZonedDateTime.AnotherTimeClient接口的类都将具有默认方法TimeClient.getZonedDateTime指定的实现。
Suppose that you extend the interface 假设您按如下方式扩展接口TimeClient as follows:TimeClient:
public interface AbstractZoneTimeClient extends TimeClient {
public ZonedDateTime getZonedDateTime(String zoneString);
}Any class that implements the interface 任何实现AbstractZoneTimeClient will have to implement the method getZonedDateTime; this method is an abstract method like all other non-default (and non-static) methods in an interface.AbstractZoneTimeClient接口的类都必须实现getZoneDateTime方法;与接口中的所有其他非默认(和非静态)方法一样,此方法是一种abstract方法。
Suppose that you extend the interface 假设您按如下方式扩展接口TimeClient as follows:TimeClient:
public interface HandleInvalidTimeZoneClient extends TimeClient {
default public ZonedDateTime getZonedDateTime(String zoneString) {
try {
return ZonedDateTime.of(getLocalDateTime(),ZoneId.of(zoneString));
} catch (DateTimeException e) {
System.err.println("Invalid zone ID: " + zoneString +
"; using the default time zone instead.");
return ZonedDateTime.of(getLocalDateTime(),ZoneId.systemDefault());
}
}
}Any class that implements the interface 实现HandleInvalidTimeZoneClient will use the implementation of getZonedDateTime specified by this interface instead of the one specified by the interface TimeClient.HandleInvalidTimeZoneClient接口的任何类都将使用此接口指定的getZonedDateTime实现,而不是TimeClient接口指定的getZonedDateTime实现。
In addition to default methods, you can define static methods in interfaces. 除了默认方法外,还可以在接口中定义静态方法。(A static method is a method that is associated with the class in which it is defined rather than with any object. Every instance of the class shares its static methods.) (静态方法是与定义它的类关联的方法,而不是与任何对象关联的方法。该类的每个实例都共享其静态方法。)This makes it easier for you to organize helper methods in your libraries; you can keep static methods specific to an interface in the same interface rather than in a separate class. 这使您更容易在库中组织助手方法;您可以将特定于接口的静态方法保留在同一接口中,而不是单独的类中。The following example defines a static method that retrieves a 下面的示例定义了一个静态方法,该方法检索与时区标识符对应的ZoneId object corresponding to a time zone identifier; it uses the system default time zone if there is no ZoneId object corresponding to the given identifier. ZoneId对象;如果没有与给定标识符对应的ZoneId对象,则使用系统默认时区。(As a result, you can simplify the method (因此,可以简化getZonedDateTime):getZonedDateTime方法):
public interface TimeClient {
// ...
static public ZoneId getZoneId (String zoneString) {
try {
return ZoneId.of(zoneString);
} catch (DateTimeException e) {
System.err.println("Invalid time zone: " + zoneString +
"; using default time zone instead.");
return ZoneId.systemDefault();
}
}
default public ZonedDateTime getZonedDateTime(String zoneString) {
return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
}
}Like static methods in classes, you specify that a method definition in an interface is a static method with the 与类中的静态方法一样,可以指定接口中的方法定义是静态方法,方法签名的开头带有static keyword at the beginning of the method signature. static关键字。All method declarations in an interface, including static methods, are implicitly 接口中的所有方法声明(包括静态方法)都是隐式公开的,因此可以省略public, so you can omit the public modifier.public修饰符。
Default methods enable you to add new functionality to existing interfaces and ensure binary compatibility with code written for older versions of those interfaces. 默认方法使您能够向现有接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。In particular, default methods enable you to add methods that accept lambda expressions as parameters to existing interfaces. 特别是,默认方法允许您将接受lambda表达式作为参数的方法添加到现有接口中。This section demonstrates how the 本节演示如何使用默认方法和静态方法增强Comparator interface has been enhanced with default and static methods.Comparator接口。
Consider the 考虑Card and Deck classes as described in Questions and Exercises: Classes. Card类和Deck类,如问题和练习:类中所描述的。This example rewrites the 本例将Card and Deck classes as interfaces. Card类和Deck类重写为接口。The Card interface contains two enum types (Suit and Rank) and two abstract methods (getSuit and getRank):Card接口包含两种enum类型(Suit和Rank)和两种抽象方法(getSuit和getRank):
package defaultmethods;
public interface Card extends Comparable<Card> {
public enum Suit {
DIAMONDS (1, "Diamonds"),
CLUBS (2, "Clubs" ),
HEARTS (3, "Hearts" ),
SPADES (4, "Spades" );
private final int value;
private final String text;
Suit(int value, String text) {
this.value = value;
this.text = text;
}
public int value() {return value;}
public String text() {return text;}
}
public enum Rank {
DEUCE (2 , "Two" ),
THREE (3 , "Three"),
FOUR (4 , "Four" ),
FIVE (5 , "Five" ),
SIX (6 , "Six" ),
SEVEN (7 , "Seven"),
EIGHT (8 , "Eight"),
NINE (9 , "Nine" ),
TEN (10, "Ten" ),
JACK (11, "Jack" ),
QUEEN (12, "Queen"),
KING (13, "King" ),
ACE (14, "Ace" );
private final int value;
private final String text;
Rank(int value, String text) {
this.value = value;
this.text = text;
}
public int value() {return value;}
public String text() {return text;}
}
public Card.Suit getSuit();
public Card.Rank getRank();
}The Deck interface contains various methods that manipulate cards in a deck:Deck接口包含操纵牌组中的牌的各种方法:
package defaultmethods;
import java.util.*;
import java.util.stream.*;
import java.lang.*;
public interface Deck {
List<Card> getCards();
Deck deckFactory();
int size();
void addCard(Card card);
void addCards(List<Card> cards);
void addDeck(Deck deck);
void shuffle();
void sort();
void sort(Comparator<Card> c);
String deckToString();
Map<Integer, Deck> deal(int players, int numberOfCards)
throws IllegalArgumentException;
}The class PlayingCard implements the interface Card, and the class StandardDeck implements the interface Deck.PlayingCard类实现了Card接口,StandardDeck类实现了Deck接口。
The class StandardDeck implements the abstract method Deck.sort as follows:StandardDeck类实现抽象方法Deck.sort,如下所示:
public class StandardDeck implements Deck {
private List<Card> entireDeck;
// ...
public void sort() {
Collections.sort(entireDeck);
}
// ...
}The method Collections.sort sorts an instance of List whose element type implements the interface Comparable. Collections.sort方法对List实例进行排序,该实例的元素类型实现了Comparable接口。The member 成员entireDeck is an instance of List whose elements are of the type Card, which extends Comparable. entireDeck是List的一个实例,其元素为Card类型,它扩展了Comparable。The class PlayingCard implements the Comparable.compareTo method as follows:PlayingCard类实现了Comparable.compareTo方法,如下所示:
public int hashCode() {
return ((suit.value()-1)*13)+rank.value();
}
public int compareTo(Card o) {
return this.hashCode() - o.hashCode();
}The method 方法compareTo causes the method StandardDeck.sort() to sort the deck of cards first by suit, and then by rank.compareTo导致方法StandardDeck.sort()首先按花色,然后按等级对卡片组进行排序。
What if you want to sort the deck first by rank, then by suit? 如果你想先按等级,然后按花色来排序,那该怎么办?You would need to implement the 你需要实现Comparator interface to specify new sorting criteria, and use the method sort(List<T> list, Comparator<? super T> c) (the version of the sort method that includes a Comparator parameter). Comparator接口,以指定新的排序条件,并使用sort(List<T> list, Comparator<? super T> c)方法(包含Comparator的sort方法的版本)。You can define the following method in the class 您可以在StandardDeck:StandardDeck类中定义以下方法:
public void sort(Comparator<Card> c) {
Collections.sort(entireDeck, c);
}With this method, you can specify how the method 使用此方法,您可以指定Collections.sort sorts instances of the Card class. Collections.sort方法如何排序Card类的实例。One way to do this is to implement the 一种方法是实现Comparator interface to specify how you want the cards sorted. Comparator接口来指定您希望如何对卡片进行排序。The example 示例SortByRankThenSuit does this:SortByRankThenSuit执行以下操作:
package defaultmethods;
import java.util.*;
import java.util.stream.*;
import java.lang.*;
public class SortByRankThenSuit implements Comparator<Card> {
public int compare(Card firstCard, Card secondCard) {
int compVal =
firstCard.getRank().value() - secondCard.getRank().value();
if (compVal != 0)
return compVal;
else
return firstCard.getSuit().value() - secondCard.getSuit().value();
}
}The following invocation sorts the deck of playing cards first by rank, then by suit:以下调用首先按等级,然后按花色对扑克牌组进行排序:
StandardDeck myDeck = new StandardDeck(); myDeck.shuffle(); myDeck.sort(new SortByRankThenSuit());
However, this approach is too verbose; it would be better if you could specify just the sort criteria and avoid creating multiple sorting implementations. 然而,这种方法过于冗长;如果您可以只指定排序标准,并避免创建多个排序实现,那就更好了。Suppose that you are the developer who wrote the 假设您是编写Comparator interface. Comparator接口的开发人员。What default or static methods could you add to the 您可以向Comparator interface to enable other developers to more easily specify sort criteria?Comparator接口添加哪些默认或静态方法,以使其他开发人员能够更轻松地指定排序标准?
To start, suppose that you want to sort the deck of playing cards by rank, regardless of suit. 首先,假设您希望按等级对扑克牌组进行排序,而不考虑花色。You can invoke the 您可以按如下方式调用StandardDeck.sort method as follows:StandardDeck.sort方法:
StandardDeck myDeck = new StandardDeck();
myDeck.shuffle();
myDeck.sort(
(firstCard, secondCard) -> firstCard.getRank().value() - secondCard.getRank().value()
);Because the interface 因为Comparator is a functional interface, you can use a lambda expression as an argument for the sort method. Comparator接口是一个函数接口,所以可以使用lambda表达式作为sort方法的参数。In this example, the lambda expression compares two integer values.在本例中,lambda表达式比较两个整数值。
It would be simpler for your developers if they could create a 如果您的开发人员可以仅通过调用Comparator instance by invoking the method Card.getRank only. Card.getRank方法来创建Comparator实例,这对他们来说会更简单。In particular, it would be helpful if your developers could create a 特别是,如果开发人员可以创建一个比较器实例来比较任何可以从Comparator instance that compares any object that can return a numerical value from a method such as getValue or hashCode. getValue或hashCode等方法返回数值的对象,这将非常有用。The Comparator interface has been enhanced with this ability with the static method comparing:Comparator接口已通过静态方法comparing增强:
myDeck.sort(Comparator.comparing((card) -> card.getRank()));
In this example, you can use a method reference instead:在本例中,您可以改为使用方法引用:
myDeck.sort(Comparator.comparing(Card::getRank));
This invocation better demonstrates how to specify different sort criteria and avoid creating multiple sorting implementations.此调用更好地演示了如何指定不同的排序标准并避免创建多个排序实现。
The Comparator interface has been enhanced with other versions of the static method comparing such as comparingDouble and comparingLong that enable you to create Comparator instances that compare other data types.Comparator接口已通过其他版本的静态方法comparing得到了增强,例如comparingDouble和comparingLong,它们使您能够创建比较其他数据类型的Comparator实例。
Suppose that your developers would like to create a 假设开发人员希望创建一个Comparator instance that could compare objects with more than one criteria. Comparator实例,该实例可以比较具有多个条件的对象。For example, how would you sort the deck of playing cards first by rank, and then by suit? 例如,你会如何首先按等级,然后按花色对一副扑克牌进行排序?As before, you could use a lambda expression to specify these sort criteria:如前所述,可以使用lambda表达式指定以下排序条件:
StandardDeck myDeck = new StandardDeck();
myDeck.shuffle();
myDeck.sort(
(firstCard, secondCard) -> {
int compare =
firstCard.getRank().value() - secondCard.getRank().value();
if (compare != 0)
return compare;
else
return firstCard.getSuit().value() - secondCard.getSuit().value();
}
);It would be simpler for your developers if they could build a 如果开发人员可以从一系列Comparator instance from a series of Comparator instances. Comparator实例构建Comparator实例,那么对他们来说会更简单。The Comparator interface has been enhanced with this ability with the default method thenComparing:Comparator接口使用默认方法thenComparing增强了此功能:
myDeck.sort(
Comparator
.comparing(Card::getRank)
.thenComparing(Comparator.comparing(Card::getSuit)));The Comparator interface has been enhanced with other versions of the default method thenComparing (such as thenComparingDouble and thenComparingLong) that enable you to build Comparator instances that compare other data types.Comparator接口已通过默认方法thenComparing(譬如thenComparingDouble和thenComparingLong)的其他版本进行了增强,使您能够构建比较其他数据类型的Comparator实例。
Suppose that your developers would like to create a 假设开发人员希望创建一个Comparator instance that enables them to sort a collection of objects in reverse order. Comparator实例,使他们能够按相反顺序对对象集合进行排序。For example, how would you sort the deck of playing cards first by descending order of rank, from Ace to Two (instead of from Two to Ace)? 例如,您将如何首先按排名的降序对扑克牌组进行排序,从A到2(而不是从2到A)?As before, you could specify another lambda expression. 与前面一样,可以指定另一个lambda表达式。However, it would be simpler for your developers if they could reverse an existing 但是,如果开发人员可以通过调用方法来反转现有的Comparator by invoking a method. Comparator,那么对他们来说会更简单。The Comparator interface has been enhanced with this ability with the default method reversed:Comparator接口已通过默认方法reversed的功能得到增强:
myDeck.sort(
Comparator.comparing(Card::getRank)
.reversed()
.thenComparing(Comparator.comparing(Card::getSuit)));This example demonstrates how the 此示例演示如何使用默认方法、静态方法、lambda表达式和方法引用增强Comparator interface has been enhanced with default methods, static methods, lambda expressions, and method references to create more expressive library methods whose functionality programmers can quickly deduce by looking at how they are invoked. Comparator接口,以创建更具表达力的库方法,程序员可以通过查看如何调用这些方法来快速推断其功能。Use these constructs to enhance the interfaces in your libraries.使用这些构造增强库中的接口。