Default java что это
Defaut method в Java
Default-методы появились Java 8. В это статье рассказывается, что это такое, зачем появилось, и как ими пользоваться.
Default-метод — это метод, который реализуется прямо в интерфейсе, его помечают ключевым словом default.
Пример использования
Допустим, у нас есть интерфейс Animal:
Есть классы Cat и Fish, реализующие интерфейс Animal:
Мы хотим добавить в интерфейс Animal метод sleep(), при этом не реализовывать его в каждом классе, а реализовать непосредственно в интерфейсе. Классы же будут наследовать этот метод по умолчанию. Для этого наш метод надо обозначить как default:
Теперь этот метод унаследуют все животные:
Впрочем, его можно и переопределить в каком-либо из классов, например в Fish:
Убедимся, что рыба спит по-своему:
Как наследуются default-методы
Возникает вопрос, какой метод унаследует класс, реализующий два интерфейса, если оба из них содержат default-методы с одинаковыми именами.
Например, есть второй интерфейс Man, который тоже содержит свой default метод sleep():
И есть класс Kentavr, реализующий как интерфейс Man, так и Animal. Какой же метод sleep() унаследует Kentavr?
Чтобы не было неопределенности (и чтобы скомпилировался код), мы обязаны переопределить в Kentavr метод sleep(), причем можно просто вызвать в нем метод sleep() любого из интерфейсов — Man либо Animal, указав через точку и super, чей именно метод нужен:
Убедимся, что кентавр спит по-человечески:
Причины появления default-методов
Наверно уже понятно, что default-методы упрощают рефакторинг — а именно, добавление новых методов.
До Java 8 все методы в интерфейсах были абстрактными. К чему это вело?
К тому, что при добавлении нового метода в интерфейс приходилось править все классы, реализующие интерфейс — реализовывать метод в этих классах. Это было неудобно. А в Java 8 (в классы ядра) захотели ввести новые методы в старые интерфейсы. Так что ввели ключевое слово default и эти методы сделали default. Например, в интерфейсе java.lang.Iterable появились новые default-методы forEach() и spliterator():
Мы рассмотрели, что такое default метод в Java. Код примеров доступен на GitHub.
Java 8 Default и Static методы
Данная статья написана командой Vertex Academy. Это одна из статей из нашего Учебника по Java 8. Надеемся, что данная статья Вам будет полезна. Приятного прочтения!
1. Введение
В Java 8 появилась возможность писать методы с реализацией в интерфейсах. Такие методы называются default и static. Особенности работы с ними мы сегодня и рассмотрим.
2. Default методы
Объявляются такие методы следующим образом
Использовать их можно только после получения экземпляра
Default методы так же можно переопределять как и обычные методы
Но есть один нюанс, рассмотрим следующий случай
В данном случае мы получим ошибку компиляции, ибо нельзя наследовать одновременно несколько default методов, не понятно какой из методов вызывать, если мы напишем следующий код
3. Static методы
C ними все проще, чем с default методами. Static методы в интерфейсе работают точно так же как и static методы в классе.
Наследовать static методы в интерфейсе нельзя, как и static методы в классах.
Так же нельзя вызывать static метод интерфейса из класса наследника
В Java 8 есть еще множество полезных нововведений, которые можно найти тут
Есть возможность записаться на наши курсы по Java. Детальную информацию смотрите у нас на сайте.
Новое в Java 8
Методы интерфейсов по умолчанию
Лямбда-выражения
Давайте начнем с простого примера: сортировка массива строк в предыдущих версиях языка.
Статический метод Collections.sort принимает список и компаратор, который используется для сортировки списка. Наверняка вам часто приходилось создавать анонимные компараторы для того чтобы передать их в метод.
Java 8 предоставляет гораздо более короткий синтаксис — лямбда-выражения, чтобы вам не приходилось тратить время на создание анонимных объектов:
Как видите, код гораздо короче и куда более читаем. И его можно сделать еще короче:
Компилятору известны типы параметров, поэтому их можно тоже опустить. Давайте посмотрим, как еще могут использовать лямбда-выражения.
Функциональные интерфейсы
Как лямбда-выражения соответствуют системе типов языка Java? Каждой лямбде соответствует тип, представленный интерфейсом. Так называемый функциональный интерфейс должен содержать ровно один абстрактный метод. Каждое лямбда-выражение этого типа будет сопоставлено объявленному методу. Также, поскольку методы по умолчанию не являются абстрактными, вы можете добавлять в функциональный интерфейс сколько угодно таких методов.
Ссылки на методы и конструкторы
Предыдущий пример можно упростить, если использовать статические ссылки на методы:
Давайте посмотрим, как передавать ссылки на конструкторы. Сперва определим бин с несколькими конструкторами:
Затем определим интерфейс фабрики, которая будет использоваться для создания новых персон:
Теперь вместо реализации интерфейса мы соединяем все вместе при помощи ссылки на конструктор:
Области действия лямбд
Однако переменная num должна все равно оставаться неизменяемой. Следующий код не скомпилируется:
Запись в переменную num в пределах лямбда-выражения также запрещена.
Доступ к полям и статическим переменным
В отличии от локальных переменных, мы можем записывать значения в экземплярные поля класса и статические переменные внутри лямбда-выражений. Это поведение хорошо знакомо по анонимным объектам.
Доступ к методам интерфейсов по умолчанию
Внутри лямбда-выражений запрещено обращаться к методам по умолчанию. Следующий код не скомпилируется:
Встроенные функциональные интерфейсы
Однако в Java 8 также появилось много новых функциональных интерфейсов, которые призваны облегчить вам жизнь. Некоторые интерфейсы хорошо известны по библиотеке Google Guava. Даже если вы незнакомы с этой библиотекой, вам стоить взглянуть, как эти интерфейсы были дополнены некоторыми полезными методами расширений.
Предикаты
Функции
Поставщики
Поставщики (suppliers) предоставляют результат заданного типа. В отличии от функций, поставщики не принимают аргументов.
Потребители
Потребители (consumers) представляют собой операции, которые производятся на одним входным аргументом.
Компараторы
Компараторы хорошо известны по предыдущим версиям Java. Java 8 добавляет в интерфейс различные методы по умолчанию.
Опциональные значения
Опциональные значения (optionals) не являются функциональными интерфейсами, однако являются удобным средством предотвращения NullPointerException. Это важная концепция, которая понадобится нам в следующем разделе, поэтому давайте взглянем, как работают опциональные значения.
Опциональные значение — это по сути контейнер для значения, которое может быть равно null. Например, вам нужен метод, который возвращает какое-то значение, но иногда он должен возвращать пустое значение. Вместо того, чтобы возвращать null, в Java 8 вы можете вернуть опциональное значение.
Потоки
Сначала давайте посмотрим, как работать с потоком последовательно. Сперва создадим источник в виде списка строк:
Filter
Операция Filter принимает предикат, который фильтрует все элементы потока. Эта операция является промежуточной, т.е. позволяет нам вызвать другую операцию (например, forEach ) над результатом. ForEach принимает функцию, которая вызывается для каждого элемента в (уже отфильтрованном) поток. ForEach является конечной операцией. Она не возращает никакого значения, поэтому дальнейший вызов потоковых операций невозможен.
Sorted
Операция Sorted является промежуточной операцией, которая возвращает отсортированное представление потока. Элементы сортируются в обычном порядке, если вы не предоставили свой компаратор:
Помните, что sorted создает всего лишь отсортированное представление и не влияет на порядок элементов в исходной коллекции. Порядок строк в stringCollection остается нетронутым:
Match
Для проверки, удовлетворяет ли поток заданному предикату, используются различные операции сопоставления (match). Все операции сопоставления являются конечными и возвращают результат типа boolean.
Count
Reduce
Эта конечная операция производит свертку элементов потока по заданной функции. Результатом является опциональное значение.
Параллельные потоки
Как уже упоминалось выше, потоки могут быть последовательными и параллельными. Операции над последовательными потоками выполняются в одном потоке процессора, над параллельными — используя несколько потоков процессора.
Следующие пример демонстрирует, как можно легко увеличить скорость работы, используя параллельные потоки.
Сперва создадим большой список из уникальных элементов:
Теперь измерим время сортировки этого списка.
Последовательная сортировка
Параллельная сортировка
Ассоциативные массивы
Как уже упоминалось, ассоциативные массивы (maps) не поддерживают потоки. Вместо этого ассоциативные массивы теперь поддерживают различные полезные методы, которые решают часто встречаемые задачи.
Этот код в особых комментариях не нуждается: putIfAbsent позволяет нам не писать дополнительные проверки на null; forEach принимает потребителя, который производит операцию над каждым элементом массива.
Этот код показывает как использовать для вычислений код при помощи различных функций:
Затем мы узнаем, как удалить объект по ключу, только если этот объект ассоциирован с ключом:
Еще один полезный метод:
Объединить записи двух массивов? Легко:
В случае отсутствия ключа Merge создает новую пару ключ-значение. В противном случае — вызывает функцию объединения для существующего значения.
API для работы с датами
Clock
Часовые пояса
LocalTime
Тип LocalTime представляет собой время с учетом часового пояса, например, 10pm или 17:30:15. В следующем примере создаются два местных времени для часовых поясов, определенных выше. Затем оба времени сравниваются, и вычисляется разница между ними в часах и минутах.
Тип LocalTime содержит различные фабричные методы, которые упрощают создание новых экземпляров, а также парсинг строк.
LocalDate
Создание экземпляра LocalDate путем парсинга строки:
LocalDateTime
Форматирование даты-времени работает так же, как и форматирование даты или времени. Мы можем использовать библиотечные или свои собственные шаблоны.
Подробно о синтаксисе шаблонов можно почитать здесь.
Аннотации
Аннотации в Java 8 являются повторяемыми. Давайте сразу посмотрим пример, чтобы понять, что это такое.
Сперва мы определим аннотацию-обертку, которая содержит массив аннотаций:
Вариант 1: использовать аннотацию-контейнер (старый способ)
Вариант 2: использовать повторяемую аннотацию (новый способ)
Более того, аннотации в Java 8 можно использовать еще на двух элементах:
Вот и все
Полный исходный код статьи доступен на GitHub.
Статические и стандартные методы в интерфейсах Java
Узнайте, как писать и использовать статические методы и методы по умолчанию в интерфейсах Java.
1. Обзор
В этой статье мы подробно обсудим как использовать статические и стандартные методы в интерфейсах и рассмотрим некоторые случаи использования, когда они могут быть полезны.
2. Зачем нужны Методы по умолчанию в Интерфейсах
Как и обычные методы интерфейса, методы по умолчанию неявно являются общедоступными — нет необходимости указывать модификатор public .
Давайте рассмотрим простой пример:
Причина, по которой методы default были включены в выпуск Java 8, довольно очевидна.
В типичном дизайне, основанном на абстракциях, где интерфейс имеет одну или несколько реализаций, если к интерфейсу добавляется один или несколько методов, все реализации также будут вынуждены их реализовать. В противном случае конструкция просто сломается.
Таким образом, обратная совместимость аккуратно сохраняется без необходимости рефакторинга разработчиков.
3. Методы интерфейса по умолчанию в действии
Скажем, что у нас есть наивный Vehicle интерфейс и только одна реализация. Их может быть больше, но давайте оставим все так просто:
И давайте напишем реализующий класс:
Наиболее типичным использованием методов по умолчанию в интерфейсах является постепенное предоставление дополнительной функциональности данному типу без разбиения реализующих классов.
Кроме того, они могут быть использованы для предоставления дополнительной функциональности вокруг существующего абстрактного метода :
4. Правила Наследования Нескольких Интерфейсов
Методы интерфейса по умолчанию-действительно довольно приятная функция, но с некоторыми оговорками, о которых стоит упомянуть. Поскольку Java позволяет классам реализовывать несколько интерфейсов, важно знать, что происходит, когда класс реализует несколько интерфейсов, определяющих одни и те же методы по умолчанию .
Чтобы лучше понять этот сценарий, давайте определим новый Сигнализация интерфейс и рефакторинг Автомобиль класс:
Чтобы устранить эту двусмысленность, мы должны явно предоставить реализацию методов:
Давайте рассмотрим пример, в котором используются методы default из интерфейса Vehicle :
Кроме того, можно даже заставить класс Car использовать оба набора методов по умолчанию :
5. Методы статического интерфейса
Чтобы понять, как статические методы работают в интерфейсах, давайте проведем рефакторинг интерфейса Vehicle и добавим к нему статический служебный метод:
Теперь предположим, что мы хотим рассчитать мощность двигателя данного транспортного средства. Мы просто вызываем метод getHorsePower() :
Идея, лежащая в основе статических интерфейсных методов, состоит в том, чтобы обеспечить простой механизм, который позволяет нам повысить степень согласованности дизайна, объединив связанные методы в одном месте без необходимости создания объекта.
Кроме того, статические методы в интерфейсах позволяют группировать связанные служебные методы без необходимости создавать искусственные служебные классы, которые являются просто заполнителями для статических методов.
6. Заключение
В этой статье мы подробно изучили использование методов static и default interface в Java 8. На первый взгляд эта функция может показаться немного неаккуратной, особенно с точки зрения объектно-ориентированного пуриста. В идеале интерфейсы не должны инкапсулировать поведение и должны использоваться только для определения общедоступного API определенного типа.
Однако, когда дело доходит до поддержания обратной совместимости с существующим кодом, методы static и default являются хорошим компромиссом.
Default Methods
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.
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):
Extending Interfaces That Contain Default Methods
When you extend an interface that contains a default method, you can do the following:
Suppose that you extend the interface TimeClient as follows:
Suppose that you extend the interface TimeClient as follows:
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.
Suppose that you extend the interface TimeClient as follows:
Static Methods
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. (As a result, you can simplify the method getZonedDateTime ):
Integrating Default Methods into Existing Libraries
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. This section demonstrates how the Comparator interface has been enhanced with default and static methods.
Consider the Card and Deck classes as described in Questions and Exercises: Classes. This example rewrites the Card and Deck classes as interfaces. The Card interface contains two enum types ( Suit and Rank ) and two abstract methods ( getSuit and getRank ):
The Deck interface contains various methods that manipulate cards in a deck:
The class StandardDeck implements the abstract method Deck.sort as follows:
The method compareTo causes the method StandardDeck.sort() to sort the deck of cards first by suit, and then by rank.
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 list, Comparator c) (the version of the sort method that includes a Comparator parameter). You can define the following method in the class StandardDeck :
With this method, you can specify how the method Collections.sort sorts instances of the Card class. One way to do this is to implement the Comparator interface to specify how you want the cards sorted. The example SortByRankThenSuit does this:
The following invocation sorts the deck of playing cards first by rank, then by suit:
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. What default or static methods could you add to the Comparator interface to enable other developers to more easily specify sort criteria?
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:
Because the interface Comparator is a functional interface, you can use a lambda expression as an argument for the sort method. In this example, the lambda expression compares two integer values.
In this example, you can use a method reference instead:
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.
Suppose that your developers would like to create a Comparator instance that could compare objects with more than one criteria. 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:
It would be simpler for your developers if they could build a Comparator instance from a series of Comparator instances. The Comparator interface has been enhanced with this ability with the default method thenComparing :
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.
Suppose that your developers would like to create a Comparator instance that enables them to sort a collection of objects in reverse order. 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)? As before, you could specify another lambda expression. However, it would be simpler for your developers if they could reverse an existing Comparator by invoking a method. The Comparator interface has been enhanced with this ability with the default method reversed :
This example demonstrates how the 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. Use these constructs to enhance the interfaces in your libraries.