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.使用这些构造增强库中的接口。