Functional Interfaces
In Java, a functional interface is an interface that contains exactly one abstract method. This means that the interface can be used as a functional type, which is a type that represents a single function or method.
Functional interfaces are an important part of Java’s functional programming features, which allow developers to write more concise and expressive code by using lambda expressions, method references, and other functional constructs.
Some examples of built-in functional interfaces in Java include:
- Runnable: Represents a task that can be executed asynchronously.
- Consumer: Represents an operation that takes in a single argument and returns no result.
- Predicate: Represents a boolean-valued function that takes in a single argument.
- Function: Represents a function that takes in one argument and produces a result.
If you want to learn more about functional interfaces in Java, check out my tutorial on Functional Interfaces in Java. This tutorial covers the basics of functional interfaces and how to create your own functional interfaces.
Dependency Scope
Dependency scope is used to limit the transitivity of a dependency and to determine when a dependency is included in a classpath.
There are 6 scopes:
- compileThis is the default scope, used if none is specified. Compile dependencies are available in all classpaths of a project. Furthermore, those dependencies are propagated to dependent projects.
- providedThis is much like , but indicates you expect the JDK or a container to provide the dependency at runtime. For example, when building a web application for the Java Enterprise Edition, you would set the dependency on the Servlet API and related Java EE APIs to scope because the web container provides those classes. A dependency with this scope is added to the classpath used for compilation and test, but not the runtime classpath. It is not transitive.
- runtimeThis scope indicates that the dependency is not required for compilation, but is for execution. Maven includes a dependency with this scope in the runtime and test classpaths, but not the compile classpath.
- testThis scope indicates that the dependency is not required for normal use of the application, and is only available for the test compilation and execution phases. This scope is not transitive. Typically this scope is used for test libraries such as JUnit and Mockito. It is also used for non-test libraries such as Apache Commons IO if those libraries are used in unit tests (src/test/java) but not in the model code (src/main/java).
- systemThis scope is similar to except that you have to provide the JAR which contains it explicitly. The artifact is always available and is not looked up in a repository.
- importThis scope is only supported on a dependency of type in the section. It indicates the dependency is to be replaced with the effective list of dependencies in the specified POM’s section. Since they are replaced, dependencies with a scope of do not actually participate in limiting the transitivity of a dependency.
Each of the scopes (except for ) affects transitive dependencies in different ways, as is demonstrated in the table below. If a dependency is set to the scope in the left column, a transitive dependency of that dependency with the scope across the top row results in a dependency in the main project with the scope listed at the intersection. If no scope is listed, it means the dependency is omitted.
compile | provided | runtime | test | |
compile | compile(*) | — | runtime | — |
provided | provided | — | provided | — |
runtime | runtime | — | runtime | — |
test | test | — | test | — |
Интерфейс «Функция» (Function Interface)
Интерфейс Function принимает входные данные и возвращает определенный тип, подобно функции с параметрами и возвращаемым типом. Первый параметр в объявлении интерфейса определяет тип входного аргумента, а второй — тип возвращаемого значения. Также существуют интерфейсы BiFunction, который принимает два входных параметра и UnaryOperator, который принимает и возвращает один и тот же тип.
Если несколько функций объединены в цепочку с использованием метода andThen, они будут выполнены по порядку слева направо. Метод compose позволяет выполнить функции в обратном порядке — справа налево.
По традиции рассмотрим пример
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class FunctionInterface { //При объявлении Функции сначала указывается тип входного аргумента, затем тип возвращаемого значения Function<String, String> toUpperCase = (text) -> text.toUpperCase(); //Принимает String, возвращает тоже String Function<String, String> toLowerCase = (text) -> text.toLowerCase(); Function<Integer, Double> log10 = (number) -> Math.log10(number); //Принимает Integer, возвращает Double //То же самое, но вместо лямбда-функций применяются ссылки на методы Function<String, String> toUpperCaseUsingMethodReference = String::toUpperCase; Function<String, String> toLowerCaseUsingMethodReference = String::toLowerCase; Function<Integer, Double> log10UsingMethodReference = Math::log10; //Пример с BiFunction (Тип 1-го входного аргумента, Тип 2-го входного аргумента, Тип возвращаемого значения) BiFunction<Integer, Integer, Integer> powerOf = (base, power) -> (int) Math.pow(base, power); //Пример с UnaryOperator (Тип входного аргумента и возвращаемого значения - одинаковые) UnaryOperator<String> appendText = (text) -> "I am appending: " + text; @BeforeEach public void setup(TestInfo testInfo) { System.out.println("Test name: " + testInfo.getDisplayName()); System.out.println(); } @Order(1) @Test public void functionTest() { String upperCaseResult = toUpperCase.apply("Hello world!"); Double log10Result = log10.apply(5000); System.out.println(upperCaseResult); System.out.println(log10Result); } @Order(2) @Test public void functionChainWithAndThen() { String chainResult1 = toUpperCase.andThen(toLowerCase).apply("heLLo WorLD!"); String chainResult2 = toLowerCase.andThen(toUpperCase).apply("heLLo WorLD secOND tiME!"); System.out.println(chainResult1); System.out.println(chainResult2); } @Order(3) @Test public void functionChainWithCompose() { String chainResult1 = toUpperCase.compose(toLowerCase).apply("heLLo WorLD!"); String chainResult2 = toLowerCase.compose(toUpperCase).apply("heLLo WorLD secOND tiME!"); System.out.println(chainResult1); System.out.println(chainResult2); } @Order(4) @Test public void biFunctionTest() { int result = powerOf.apply(5, 5); System.out.println("5-ть в 5-ой степени равно: " + result); } @Order(5) @Test public void unaryOperatorTest(){ System.out.println(appendText.apply("Hello world with Unary Operator!")); } }
Результат выполнения:
1) Метод functionTest():
Test name: functionTest "HELLO WORLD!" 3.698970004336019.
2) Во время выполнения тестового метода functionChainWithAndThen() два экземпляра функции (toUpperCase и toLowerCase) объединяются с помощью метода andThen(), и результирующая функция применяется к входным значениям. Результатом будет строка «hello world!» и строка «HELLO WORLD SECOND TIME!».
3) В тестовом методе functionChainWithCompose() два экземпляра Function (toUpperCase и toLowerCase) объединяются с помощью метода compose(), и полученная функция применяется к входным значениям. Метод compose() похож на andThen(), но он применяет вначале вторую функцию, а затем первую. Поэтому результатом будет строка «HELLO WORLD!» и строка «hello world second time!».
4) Результат выполнения метода biFunctionTest():
Test name: biFunctionTest 5-ть в 5-ой степени равно: 3125
5) Результат выполнения метода unaryOperatorTest():
Test name: unaryOperatorTest I am appending: Hello world with Unary Operator!
Функциональные интерфейсы Java являются мощным инструментом, позволяющим разработчикам писать производительный и компонуемый код. Мы рассмотрели четыре наиболее популярных функциональных интерфейса: Поставщик (Supplier), Потребитель (Consumer), Предикат (Predicate) и Функция (Function).
Надеемся, что эта статья помогла вам лучше понять концепции функционального программирования в Java и вы сможете применить их на практике, создавая более эффективный, читаемый и поддерживаемый код.
Что такое интерфейс?
Большинство современных языков вроде C#, TypeScript или Java поддерживают интерфейсы. Сами же интерфейсы достаточно сложно описать, тем не менее я попытаюсь. Словарь даёт следующее определение:
Интерфейс — общая точка взаимодействия двух систем, субъектов, организаций и пр.
В коде же интерфейсы — это то, как модули (обычно классы) взаимодействуют друг с другом для выполнения заданных действий. Код выполняет определённые действия, а интерфейсы — это определения, позволяющие его модулям работать сообща. Интерфейсы — это абстрактное определение функциональности.
Звучит это всё заумно, но так оно и есть.
Объявление интерфейса определяет набор свойств и методов, которые вы можете использовать для конкретной цели. Для этого интерфейсам не требуется никакой особой реализации. Они определяют, что может делать класс и ограничивают его внешнее представление заданными в них параметрами.
Конечно, сам интерфейс должен быть реализован, чтобы его использовать. Когда же вы используете интерфейс, то вас уже абсолютно не волнует то, как он реализован. Вы просто знаете, что можете вызывать методы и свойства, определённые в нем, получая нужную вам функциональность. Одна из главных задач интерфейсов — это сокрытие реализации (т.е. абстрагирование).
Implementing an Interface
Before you can really use an interface, you must implement that interface in some Java class.
Here is a class that implements the interface shown above:
public class MyInterfaceImpl implements MyInterface { public void sayHello() { System.out.println(MyInterface.hello); } }
Notice the part of the above
class declaration. This signals to the Java compiler that the
class implements the interface.
A class that implements an interface must implement all the methods declared in the interface.
The methods must have the exact same signature (name + parameters) as declared in the interface.
The class does not need to implement (declare) the variables of an interface. Only the methods.
Interface Instances
Once a Java class implements an Java interface you can use an instance of that class as an instance of
that interface. Here is an example:
MyInterface myInterface = new MyInterfaceImpl(); myInterface.sayHello();
Notice how the variable is declared to be of the interface type
while the object created is of type . Java allows this because the class
implements the interface. You can then
reference instances of the class as instances of the
interface.
You cannot create instances of a Java interface by itself. You must always create an instance of some class
that implements the interface, and reference that instance as an instance of the interface.
Conclusion
Interfaces are a powerful feature in Java that enable us to define contracts or behaviors that classes can implement. In this tutorial, we have covered the basics of defining, implementing, and extending interfaces, as well as functional interfaces and the use of default and static methods. By leveraging the power of interfaces, we can create more flexible and reusable code, enabling us to build robust and scalable Java applications.
As you continue to develop your skills in Java programming, remember to explore the full potential of interfaces and other advanced language features to help you write more efficient and elegant code.
Чего Мы Можем Достичь, используя Их?
3.1. Поведенческая функциональность
Мы используем интерфейсы для добавления определенных поведенческих функций, которые могут использоваться несвязанными классами. Например, Сопоставимый , Компаратор и Клонируемый являются интерфейсами Java , которые могут быть реализованы несвязанными классами. Ниже приведен пример Компаратора интерфейса , который используется для сравнения двух экземпляров класса Сотрудник :
public class Employee { private double salary; public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } } public class EmployeeSalaryComparator implements Comparator { @Override public int compare(Employee employeeA, Employee employeeB) { if (employeeA.getSalary() < employeeB.getSalary()) { return -1; } else if (employeeA.getSalary() > employeeB.getSalary()) { return 1; } else { return 0; } } }
Для получения дополнительной информации, пожалуйста, посетите наш учебник по Comparator и Comparable на Java.
3.2. Множественное Наследование
Классы Java поддерживают сингулярное наследование. Однако, используя интерфейсы, мы также можем реализовать несколько наследований.
Например, в приведенном ниже примере мы замечаем, что Car класс реализует интерфейсы Fly и Transform . Поступая таким образом, он наследует методы fly и transform :
public interface Transform { void transform(); } public interface Fly { void fly(); } public class Car implements Fly, Transform { @Override public void fly() { System.out.println("I can Fly!!"); } @Override public void transform() { System.out.println("I can Transform!!"); } }
3.3. Полиморфизм
Давайте начнем с вопроса: что такое полиморфизм ? Это способность объекта принимать различные формы во время выполнения. Чтобы быть более конкретным, это выполнение метода переопределения, связанного с определенным типом объекта во время выполнения.
В Java мы можем достичь полиморфизма с помощью интерфейсов. Например, интерфейс Shape может принимать различные формы — это может быть Круг или Квадрат.
Давайте начнем с определения Формы интерфейса:
public interface Shape { String name(); }
Теперь давайте также создадим класс Circle :
public class Circle implements Shape { @Override public String name() { return "Circle"; } }
А также Квадрат класс:
public class Square implements Shape { @Override public String name() { return "Square"; } }
Наконец, пришло время увидеть полиморфизм в действии, используя наш интерфейс Shape и его реализации. Давайте создадим экземпляры некоторых объектов Shape , добавим их в Список , и, наконец, напечатаем их имена в цикле:
List shapes = new ArrayList<>(); Shape circleShape = new Circle(); Shape squareShape = new Square(); shapes.add(circleShape); shapes.add(squareShape); for (Shape shape : shapes) { System.out.println(shape.name()); }
Interface Default Methods
Before Java 8, we had no way to direct an Interface to have a particular method implementation. This lead to lot of confusion and code breaks if an Interface definition is suddenly changed.
Suppose, you wrote an open source library, which contains an Interface. Say, your clients, i.e. practically all developers around the world, are using it heavily and are happy. Now you have had to upgrade the library by adding a new method definition to the Interface to support a new feature. But that would break all builds since all Classes implementing that Interface have to change now. What a catastrophe!
Thankfully, Java 8 now provides us methods for Interfaces. A method can contain its own implementation directly within the Interface! So, if a Class does not implement a default method, the compiler will take the implementation mentioned within the Interface. Nice, isn’t it? So in your library, you may add any number of default methods in interfaces without the fear of breaking anything!
But, what happens if two interfaces have the same method signature?
Awesome question. In that case, if you do not provide the implementation in the Class, poor compiler will get confused and simply fail! You have to provide a default method implementation within the Class also. There is also a nifty way using to call which implementation you like:
Определения
Информатика
В информатике имплементация — это реализация технической спецификации или алгоритма в виде программы, программного компонента или другой компьютерной системы посредством компьютерного программирования и развертывания. Для данной спецификации или стандарта может существовать множество имплементаций. Например, веб-браузеры содержат имплементации рекомендованных консорциумом World Wide Web спецификаций, а средства разработки программного обеспечения содержат имплементации языков программирования.
Особый случай возникает в объектно-ориентированном программировании, когда конкретный класс реализует интерфейс; в этом случае конкретный класс является имплементацией интерфейса и включает в себя методы, которые являются имплементациями тех методов, которые указаны интерфейсом.
Информационные технологии
В области информационных технологий в промышленности внедрение относится к послепродажному процессу руководства клиентом от покупки до использования программного или аппаратного обеспечения, которое было приобретено. Это включает в себя анализ требований, анализ области, настройки, системную интеграцию, пользовательские политики, обучение пользователей и доставку. Эти шаги часто контролируются менеджером проекта с использованием методологий управления проектами. В имплементации программного обеспечения участвуют несколько специалистов, которые являются относительно новыми для экономики, основанной на знаниях, такие как бизнес-аналитики, технические аналитики, архитекторы решений и менеджеры проектов.
Для успешного внедрения системы многие взаимосвязанные задачи должны выполняться в соответствующей последовательности. Использование хорошо зарекомендовавшей себя методологии внедрения и привлечение профессиональных консультаций может помочь, но часто именно количество задач, плохое планирование и недостаточные ресурсы вызывают проблемы с проектом внедрения, а не какие-либо из задач, которые особенно сложны. Как и в случае с культурными вопросами, отсутствие адекватных консультаций и двустороннего общения часто препятствует достижению желаемых результатов.
Политология
В политической науке имплементация относится к проведению государственной политики. Законодательные органы принимают законы, которые затем выполняются государственными служащими, работающими в бюрократических учреждениях. Этот процесс состоит из нормотворчества, нормотворчества и вынесения судебных решений. Факторы, влияющие на имплементацию, включают в себя законодательные намерения, административный потенциал имплементационной бюрократии, деятельность групп интересов и оппозицию, а также президентскую или исполнительную поддержку.
В международных отношениях осуществление относится к этапу заключения международных договоров. Он представляет собой этап, когда международные положения принимаются внутри страны посредством законодательства и регулирования. Этап осуществления отличается от ратификации международного договора.
Социальные и медицинские науки
Имплементация определяется как определенный набор действий, предназначенных для реализации на практике деятельности или программы известных измерений. Согласно этому определению, процессы имплементации являются целенаправленными и описаны достаточно подробно, чтобы независимые наблюдатели могли обнаружить наличие и силу «конкретного набора мероприятий», связанных с имплементацией. Кроме того, осуществляемая деятельность или программа описывается достаточно подробно, чтобы независимые наблюдатели могли обнаружить ее присутствие и силу.
В информатике внедрение приводит к программному обеспечению, в то время как в социальных и медицинских науках наука о внедрении изучает, как программное обеспечение может быть применено на практике или в рутинном использовании.
Водные и природные ресурсы
В области водных и природных ресурсов имплементация относится к актуализации лучших методов управления с конечными целями сохранения природных ресурсов и повышения качества водных объектов.
Интерфейсы для плагинов
Самый простой пример — plug-in. Помню, до появления интерфейсов в Delphi приходилось извращаться и одно из таких извращений я показывал в первой книге Delphi глазами хакера. Тогда Delphi не поддерживал интерфейсов, когда я писал тот пример.
Что такое plug-in? Это какой-то объект, который выполняет отделенные действия. Основной программе (основному коду) плевать, как выполняется действие и возможно, что даже по барабану, что там происходит, наша задача всего лишь предоставить возможность плагину зарегистрироваться у нас, и нам нужно знать, как можно запустить плагин на выполнение.
Итак, нам нужно описать протокол, как буду общаться между собой код и расширение плагин. Для этого описываем интерфейс:
interface IInterface
{
void getTest();
}
Сразу же хочу извинится за возможные отпечатки и ошибки в коде. Я пишу эту заметку на iPad в OneNote, у которого нет компиляторе и проверки на ошибки в C# коде.
Это всего лишь интерфейс с одним методом и он абсолютно ничего не делает и у него нет никакой реализации метода getTest. Вот тут у многих возникает вопрос — и на фиг это нужно? Не лучше ли объявить абстрактный класс и наследовать его? А не торопитесь, все самое интересное впереди.
Теперь мы можем объявить класс, который будет описывать дом и этот дом может реализовывать наш протокол:
class Home : Interface
{
public void GetTest()
{
// вот тут находится реализация интерфейсы
}
}
Точно так же, как классы наследуют другие классы, они могут наследовать и интерфейсы. В этом случае дом наследует интерфейс. Для того, чтобы такой класс корректным, у него должны бать все методы, которые объявлены в протоколе, причем они должны быть открытыми, иначе от них толку ноль.
Теперь мы можем создать интерфейс класса Home:
IInterface test = new Home();
Так как дом реализует наш протокол, то такая операция абсолютно легальна.
Пока никакой выгоды особо не видно, но теперь мы подошли к тому моменту, когда когда уже можно увидеть выгоду. Дело в том, что в C# двойное расследование запрещено. А что, если наш plugin должен наследоваться от какого-то класса? Если вы хотите реализовать расширения в виде абстрактного базового класса, то люди, которые будут писать расширения не смогут объявить класс, который будет наследовать ваш класс и класс, который им нужен. Нужно будет использовать извращения, которые не стоят выделки.
Так что нам не нужен абстрактный класс, нам нужен именно интерфейс. В этом случае программист сможет написать любой свой класс, реализовать наш интерфейс и все будет работать.
Посмотрим на полноценный код возможного примера:
using System; // классика
using System.Collections.Generic; // нам понадобится List
// возможно я здесь забыл что-то еще подключить и код не скомпилируется
// ну ничего, тем, кто любит искать ошибки, будет чем заняться
namespace OurApplication
{
// объявляем интерфейс
interface IInterface
{
void getTest();
}
// объявляем дом
class Home : IInterface
{
public void getTest()
{
}
}
// еще один класс утка, который реализует интерфейс
class Duck : IInterface
{
public void getTest()
{
}
}
// это началась наша программа
class Program
{
static void Main(string[] args)
{
// создаем список расширений
Listtests = new List();
// добавляем в него объекты
tests.Add(new Home());
tests.Add(new Duck());
// запускаем каждый объект на выполнение
foreach (IInterface test in tests)
test.getTest();
}
}
}
В этом примере мы создали два совершенно разных класса — дом и утку. Они могу происходить от любых других классов и могут быть совершенно разными, но они все же схожи в том, что они реализуют один и тот же протокол (интерфейс), а это все, что нам нужно.
В своей программе мы можем создать список из интерфейсов:
Listtests = new List();
Это список, который состоит из объектов любого класса, но все они реализуют интерфейс IInterface.
После этого я создаю утку и дом, добавляю из в список и запускаю цикл, в котором выполняют метод getTest.
Правила Наследования интерфейса
Чтобы добиться множественного наследования через интерфейсы, мы должны помнить несколько правил. Давайте рассмотрим это подробно.
5.1. Интерфейс, Расширяющий Другой Интерфейс
Когда интерфейс расширяет другой интерфейс, он наследует все абстрактные методы этого интерфейса. Давайте начнем с создания двух интерфейсов, Имеющих цвет и Форму :
public interface HasColor { String getColor(); } public interface Box extends HasColor { int getHeight() }
В приведенном выше примере Поле наследуется от Имеет цвет с использованием ключевого слова расширяется. Таким образом, интерфейс Box наследует getColor . В результате интерфейс Box теперь имеет два метода: getColor и getHeight .
5.2. Абстрактный класс, реализующий интерфейс
Когда абстрактный класс реализует интерфейс, он наследует все свои абстрактные методы и методы по умолчанию. Давайте рассмотрим интерфейс Transform и абстрактный класс Транспортное средство , который его реализует:
public interface Transform { void transform(); default void printSpecs(){ System.out.println("Transform Specification"); } } public abstract class Vehicle implements Transform {}
В этом примере класс Vehicle наследует два метода: метод abstract transform и метод по умолчанию Спецификации печати .
Реализация
Давайте перейдем к реализации и подробно рассмотрим , и множественное наследование.
3.1.
Начнем с создания класса с , и . Этот класс будет действовать как базовый класс. и расширят функциональность этого класса:
Теперь давайте создадим еще один класс с именем , который класс , наследуя его свойства Кроме того, у него есть свои собственные свойства, такие как и :
Точно так же класс также класс и будет иметь свои собственные дополнительные свойства, такие как и :
Давайте создадим объекты для базового и производного классов, чтобы посмотреть на унаследованные свойства:
Все три класса выводят связанные свойства:
3.2.
Чтобы понять абстракцию и интерфейсы, мы создадим интерфейс с двумя методами, называемыми и Как упоминалось ранее, все методы в этом интерфейсе являются абстрактными. Другими словами, интерфейс содержит только объявления методов.
В Java интерфейсы не должны явно объявлять метод как или . Классы, реализующие интерфейс , будут определять следующие методы:
Класс MediaPlayer определяет методы и для аудиофайлов:
Точно так же и предоставляет определение метода для и видео:
Далее создадим экземпляры и и вызовем методы и для них обоих:
и вызывают свои соответствующие реализации и :
3.3. Множественное наследование
Java не поддерживает множественное наследование напрямую из-за неоднозначности. Проблема неоднозначности возникает, когда класс наследуется более чем от одного родительского класса, и оба родительских класса имеют метод или свойство с одинаковым именем. Следовательно, дочерний класс не может разрешить конфликт наследуемого метода или свойства. Однако класс может наследовать от нескольких интерфейсов. Создадим интерфейс :
Класс и и определяет методы, объявленные в обоих интерфейсах:
Теперь мы создадим экземпляр класса и вызовем все реализованные методы:
Как и ожидалось, вызывает свои реализации и :