Java8 Lambdas против анонимных классов
Так как Java8 была недавно выпущена, и ее совершенно новые лямбда-выражения выглядят действительно круто, мне было интересно, означает ли это упадок классов Anonymous, к которым мы так привыкли.
Я немного исследовал эту тему и нашел несколько интересных примеров того, как лямбда-выражения будут систематически заменять эти классы, например, метод сортировки Collection, который используется для получения анонимного экземпляра Comparator для выполнения сортировки:
Collections.sort(personList, new Comparator<Person>(){
public int compare(Person p1, Person p2){
return p1.firstName.compareTo(p2.firstName);
}
});
Теперь можно сделать с помощью Lambdas:
Collections.sort(personList, (Person p1, Person p2) -> p1.firstName.compareTo(p2.firstName));
И выглядит на удивление лаконично. Поэтому мой вопрос: есть ли причина продолжать использовать эти классы в Java8 вместо Lambdas?
РЕДАКТИРОВАТЬ
Тот же вопрос, но в противоположном направлении, каковы преимущества использования Lambdas вместо классов Anonymous, поскольку Lambdas можно использовать только с интерфейсами с одним методом, является ли эта новая функция только ярлыком, используемым только в нескольких случаях, или она действительно полезна?
10 ответов
Анонимный внутренний класс (AIC) может использоваться для создания подкласса абстрактного класса или конкретного класса. AIC также может предоставить конкретную реализацию интерфейса, включая добавление состояния (полей). Экземпляр AIC может быть передан с использованием this
в телах его методов, поэтому к нему могут быть вызваны дополнительные методы, его состояние может изменяться с течением времени и т. д. Ни один из них не относится к лямбдам.
Я предполагаю, что в большинстве случаев использование AIC обеспечивало реализацию отдельных функций без сохранения состояния, поэтому их можно заменить на лямбда-выражения, но есть и другие варианты использования AIC, для которых нельзя использовать лямбды. АИК здесь, чтобы остаться.
ОБНОВИТЬ
Другое различие между AIC и лямбда-выражениями состоит в том, что AIC вводят новую область видимости. То есть имена разрешаются из суперклассов и интерфейсов AIC и могут скрывать имена, которые встречаются в окружении, вмещающем лексику. Для лямбд все имена разрешены лексически.
Лямбда, хотя и отличная функция, будет работать только с типами SAM. То есть взаимодействует только с одним абстрактным методом. Он потерпит неудачу, как только ваш интерфейс будет содержать более 1 абстрактного метода. Вот где анонимные классы будут полезны.
Итак, нет, мы не можем просто игнорировать анонимные классы. И просто к вашему сведению, ваш sort()
метод можно упростить, пропустив объявление типа для p1
а также p2
:
Collections.sort(personList, (p1, p2) -> p1.firstName.compareTo(p2.firstName));
Вы также можете использовать ссылку на метод здесь. Либо вы добавляете compareByFirstName()
метод в Person
класс и использовать:
Collections.sort(personList, Person::compareByFirstName);
или добавить геттер для firstName
Прямо получить Comparator
от Comparator.comparing()
метод:
Collections.sort(personList, Comparator.comparing(Person::getFirstName));
Лямбда-представление с анонимными классами
При запуске приложения каждый файл класса должен быть загружен и проверен.
Анонимные классы обрабатываются компилятором как новый подтип для данного класса или интерфейса, поэтому для каждого будет создан новый файл класса.
Лямбды отличаются при генерации байт-кода, они более эффективны, используют динамические инструкции, которые поставляются с JDK7.
Для лямбда-выражений эта инструкция используется для задержки перевода лямбда-выражения в байт-код до выполнения. (инструкция будет вызываться только в первый раз)
В результате лямбда-выражение станет статическим методом (созданным во время выполнения). (Существует небольшая разница с состояниями и состояниями, они разрешаются с помощью сгенерированных аргументов метода)
Есть следующие отличия:
1) Синтаксис
Лямбда-выражения выглядят аккуратно по сравнению с Anonymous Inner Class (AIC)
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("in run");
}
};
Thread t = new Thread(r);
t.start();
}
//syntax of lambda expression
public static void main(String[] args) {
Runnable r = ()->{System.out.println("in run");};
Thread t = new Thread(r);
t.start();
}
2) Область
Анонимный внутренний класс - это класс, что означает, что он имеет область видимости для переменной, определенной внутри внутреннего класса.
Принимая во внимание, чтолямбда-выражение не является отдельной областью, но является частью включающей области.
Аналогичное правило применяется к super и этому ключевому слову при использовании внутри анонимного внутреннего класса и лямбда-выражения. В случае анонимного внутреннего класса это ключевое слово относится к локальной области видимости, а супер ключевое слово относится к суперклассу анонимного класса. В то время как в случае лямбда-выражения это ключевое слово относится к объекту вмещающего типа, а super будет ссылаться на суперкласс вмещающего класса.
//AIC
public static void main(String[] args) {
final int cnt = 0;
Runnable r = new Runnable() {
@Override
public void run() {
int cnt = 5;
System.out.println("in run" + cnt);
}
};
Thread t = new Thread(r);
t.start();
}
//Lambda
public static void main(String[] args) {
final int cnt = 0;
Runnable r = ()->{
int cnt = 5; //compilation error
System.out.println("in run"+cnt);};
Thread t = new Thread(r);
t.start();
}
3) Производительность
Во время выполнения анонимные внутренние классы требуют загрузки классов, выделения памяти, инициализации объекта и вызова нестатического метода, в то время как лямбда-выражение является чистой операцией времени компиляции и не требует дополнительных затрат во время выполнения. Так что производительность лямбда-выражения лучше, чем у анонимных внутренних классов. **
** Я понимаю, что это не совсем верно. Пожалуйста, обратитесь к следующему вопросу для деталей. Лямбда против анонимной производительности внутреннего класса: снижение нагрузки на ClassLoader?
Лямбда в Java 8 была введена для функционального программирования. Где вы можете избежать шаблонного кода. Я наткнулся на эту интересную статью о лямбдах.
http://radar.oreilly.com/2014/04/whats-new-in-java-8-lambdas.html
Желательно использовать лямбда-функции для простых логик. Если реализация сложной логики с использованием лямбда-выражения будет непростой задачей при отладке кода в случае возникновения проблемы.
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| | Lambdas | Anonymous classes |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Definition | An anonymous method that can be created without belonging| An inner class without a name. |
| | to any class | |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Scope of variables of main class | Available | Not available |
| (this and super keywords also) | | |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Lines of codes | Reduced the lines of code. It’s a short form of | Have more lines of code compared to lambdas |
| | anonymous class. | |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Criteria for creating | Needs to be Functional Interface, ie interface with | Can use interfaces(including Functional |
| | only one abstract method. Example : Runnable Interface | interfaces) and abstract classes to create. |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
| Example: | Runnable r = ()->{System.out.println("Hello World");}; | Runnable r = new Runnable() { |
| | | @Override |
| | | public void run() { |
| | | System.out.println("Hello World"); |
| | | } |
| | | }; |
+----------------------------------+----------------------------------------------------------+---------------------------------------------+
- Лямбда-синтаксис не требует написания очевидного кода, который может выводить Java.
- Используя
invoke dynamic
, лямбда не преобразуются обратно в анонимные классы во время компиляции (Java не требует создания объектов, просто заботится о сигнатуре метода, может связываться с методом без создания объекта - Лямбда делает больший акцент на том, что мы хотим сделать, вместо того, что мы должны сделать, прежде чем мы сможем это сделать
Лямбда-выражение охватывает только очень специфический случай анонимных внутренних классов. Вы можете считать, что классы Anonymous Inner являются надмножеством Lambdas Expression.
Lambdas Expression ⊂ Anonymous Inner classes
Есть сценарий, в котором Anno. внутренний класс можно заменить лямбда-выражением:
- Если реализуемые внутренние классы являются функциональным интерфейсом (имеющим только один абстрактный метод)
бывший:
Interface A{
public void m1();
public void m2();
}
Interface B{
public void m();
}
Class Temp{
public static void main(String[] args){
// Anonymous inner class implementation
A a = new A()
{
public void m1(){
//
}
public void m2(){
//
}
};
a.m1(); or a.m2();
// Here B is a functional Interface so we can replace the anonymous class to Lambda Expression
B b = () => { ... }
b.m();
}
}
Кроме того, есть некоторые различия в стиле компиляции обоих. Для компилятора Lambda Expression не генерируйте какой-либо другой класс, но он генерирует для Anon. Внутренний класс
Анонимные классы остаются, потому что лямбда хороша для функций с одиночными абстрактными методами, но во всех других случаях анонимные внутренние классы - ваш спаситель.
Давайте сравним разницу между лямбда-выражением и анонимным классом.
1. Синтаксис
Анонимный класс:
package com.onlyfullstack;
public class LambdaVsAnonymousClass {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Anonymous class");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
}
Lambda:
package com.onlyfullstack;
public class LambdaVsAnonymousClass {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println("Lambda Expression");
Thread thread = new Thread(runnable);
thread.start();
}
}
2. Реализация
Анонимный класс может быть использован для реализации любого интерфейса с любым количеством абстрактных методов. Лямбда-выражение будет работать только с типами SAM(Single Abstract Method). Это интерфейс только с одним абстрактным методом, который также называется функциональным интерфейсом. Он потерпит неудачу, как только ваш интерфейс будет содержать более 1 абстрактного метода.
3. Компиляция
Анонимный класс: Java создает два файла класса для Java-файла LambdaVsAnonymousClass как LambdaVsAnonymousClass.class - содержит основную программу LambdaVsAnonymousClass$1.class - содержит анонимный класс
Лямбда-выражение: С помощью лямбда-выражения компилятор создаст только 1 файл класса, как показано ниже.
Таким образом, Java создаст новый файл класса для каждого используемого класса Anonymous.
4. Производительность
Анонимные классы обрабатываются компилятором как новый подтип для данного класса или интерфейса, поэтому для каждого используемого анонимного класса будет создан новый файл класса. При запуске приложения каждый класс, созданный для класса Anonymous, будет загружен и проверен. Этот процесс довольно трудоемкий, когда у вас большое количество анонимных классов.
Лямбда-выражения Вместо генерации прямого байт-кода для лямбды (как предложенный подход к синтаксическому сахарному анонимному классу) компилятор объявляет рецепт (с помощью инструкций invokeDynamic) и делегирует реальный подход к построению во время выполнения.
Таким образом, лямбда-выражения быстрее, чем анонимные классы, так как они выполняются только при вызове.
Для получения дополнительной информации, пожалуйста, обратитесь к ссылкам ниже:
https://onlyfullstack.blogspot.com/2019/02/lambda-vs-anonymous-class-in-java-8.html
https://onlyfullstack.blogspot.com/2019/02/how-lambda-internally-works-in-java-8.html