Содержание
- 1 Недостатки Java
- 2 Кто и для чего использует Java
- 3 Usages
- 4 How to Avoid Using It?
- 5 Пункт 5: избегайте использования var, если названия переменных не содержат достаточной информации о типе для понимания кода
- 6 Suppliers
- 7 Бинарные специализации функциональных интерфейсов
- 8 7 ответов
- 9 Two-Arity Function Specializations
- 10 1.1 Объекты и классы
- 11 Functional Interfaces
- 12 Using this for Ambiguity Variable Names
- 13 Lambdas in Java 8
Недостатки Java
Всё это звучит хорошо, но есть у Java и недостатки, весьма существенные.
Не самая высокая производительность. У С и С++ есть только компилятор, который переводит программу в машинный код. У Java такого нет, и всё делает виртуальная машина. Получается, что для выполнения Java-кода нужно делать двойную работу: процессор запускает JVM, а JVM выполняет сам код. Из-за такого подхода Java-программы в полтора-два раза медленнее, чем тот же код, написанный на С++.
Платная лицензия. С 2019 года компания Oracle, которая владеет лицензией на Java, разделила её на две части: платную и бесплатную. Если вам нужна долгая поддержка среды разработки и стабильная работа программ — берите платную лицензию, иногда стабильность важнее. Если вам не нужна поддержка и вы можете установить каждую новую бесплатную сборку самостоятельно — отдавать деньги не нужно.
Громоздкий код. Разработчики Java ставили своей целью упростить программирование на С++, и им это удалось. Цена, которую пришлось за это заплатить, — большие и длинные конструкции в языке, которые часто мешают пониманию сути того, что происходит. Сравните код, который рисует пирамидку из звёздочек, на Java и Python:
Кто и для чего использует Java
Для начала — список компаний и технологий, которые используют Java в качестве основного языка программирования:
- Amazon,
- LinkedIn,
- eBay,
- Yahoo!
- OpenOffice,
- IBM, Intel и Oracle.
Разработка под Android. Java до сих пор остаётся основным языком мобильной разработки для этой операционной системы, несмотря на растущую популярность Kotlin. Android Studio — официальная среда Java-разработки, признанная компанией Google.
Терминалы и платёжные системы. Благодаря виртуальной машине Java-код может работать на оборудовании, которое стоит в платёжных терминалах и банкоматах.
Работа с финансами. Если нужен простой и надёжный сайт по обработке платежей или переводу денег — используйте Java. Встроенные системы безопасности помогут избежать несанкционированных действий программы.
Usages
There are some situations when using the Void type can be interesting.
3.1. Reflection
First, we could use it when doing reflection. Indeed, the return type of any void method will match the Void.TYPE variable that holds the Class<Void> value mentioned earlier.
Let’s imagine a simple Calculator class:
Some methods are returning an integer, some are not returning anything. Now, let’s say we have to retrieve, by reflection, all methods that don’t return any result. We’ll achieve this by using the Void.TYPE variable:
As we can see, only the clear() and print() methods have been retrieved.
3.2. Generics
Another usage of the Void type is with generic classes. Let’s suppose we are calling a method which requires a Callable parameter:
But, the Callable we want to pass doesn’t have to return anything. Therefore, we can pass a Callable<Void>:
We could have either use a random type (e.g. Callable<Integer>) and return null or no type at all (Callable), but using Void states our intentions clearly.
We can also apply this method to lambdas. As a matter of fact, our Callable could have been written as a lambda. Let’s imagine a method requiring a Function, but we want to use a Function that doesn’t return anything. Then we just have to make it return Void:
How to Avoid Using It?
Now, we’ve seen some usages of the Void type. However, even if the first usage is totally fine, we might want to avoid using Void in generics if possible. Indeed, encountering a return type that represents the absence of a result and can only contain null can be cumbersome.
We’ll now see how to avoid these situations. First, let’s consider our method with the Callable parameter. In order to avoid using a Callable<Void>, we might offer another method taking a Runnable parameter instead:
So, we can pass it a Runnable which doesn’t return any value and thus get rid of the useless return null:
But then, what if the Defer class is not ours to modify? Then we can either stick to the Callable<Void> option or create another class taking a Runnable and deferring the call to the Defer class:
By doing that, we encapsulate the cumbersome part once and for all in our own method, allowing future developers to use a simpler API.
Of course, the same can be achieved for Function. In our example, the Function doesn’t return anything, thus we can provide another method taking a Consumer instead:
Then, what if our function doesn’t take any parameter? We can either use a Runnable or create our own functional interface (if that seems clearer):
Then, we overload the defer() method again:
Пункт 5: избегайте использования var, если названия переменных не содержат достаточной информации о типе для понимания кода
Преимущество использования var заключается в написании более лаконичного кода. Например, в случае использования конструкторов, мы можем избежать необходимости повторения имени класса и, следовательно, устранить избыточность кода.
Избегайте следующего:
Вместо этого используйте:
Для конструкции, приведенной ниже, var также станет удачным способом упрощения кода без потери информативности.
Избегайте:
Используйте следующий код:
Итак, почему нам более комфортно работать с var в представленных примерах? Потому, что вся необходимая информация содержится в названиях переменных. Но если var, в сочетании с именем переменной, приводит к уменьшению ясности кода, лучше откажитесь от его использования.
Избегайте:
Используйте:
Подумаем, к примеру, над использованием класса . Этот класс имеет статический метод , который возвращает new Selector и открывает его. Но здесь запросто можно подумать, что метод может возвращать тип boolean, в зависимости от успешности открытия имеющегося селектора, или даже возвращать void. Использование здесь var приведет к потери информации и путанице в коде.
Suppliers
The Supplier functional interface is yet another Function specialization that does not take any arguments. It is typically used for lazy generation of values. For instance, let’s define a function that squares a double value. It will receive not a value itself, but a Supplier of this value:
This allows us to lazily generate the argument for invocation of this function using a Supplier implementation. This can be useful if the generation of this argument takes a considerable amount of time. We’ll simulate that using Guava’s sleepUninterruptibly method:
Another use case for the Supplier is defining a logic for sequence generation. To demonstrate it, let’s use a static Stream.generate method to create a Stream of Fibonacci numbers:
The function that is passed to the Stream.generate method implements the Supplier functional interface. Notice that to be useful as a generator, the Supplier usually needs some sort of external state. In this case, its state is comprised of two last Fibonacci sequence numbers.
To implement this state, we use an array instead of a couple of variables, because all external variables used inside the lambda have to be effectively final.
Other specializations of Supplier functional interface include BooleanSupplier, DoubleSupplier, LongSupplier and IntSupplier, whose return types are corresponding primitives.
Бинарные специализации функциональных интерфейсов
Существуют бинарные специализации (binary specializations) интерфейсов , , и , которые принимают на вход два элемента.
Интерфейс | Функциональный дескриптор | Описание |
---|---|---|
BiConsumer<T, U> | (T, U) -> void | Представляет операцию, которая принимает на вход два элемента и не возвращает результат. |
BiFunction<T, U, R> | (T, U) -> R | Представляет операцию, которая принимает на вход два элемента и возвращает результат. |
BiPredicate<T, U> | (T, U) -> boolean | Представляет операцию, которая принимает на вход два элемента и возвращает значение . |
BinaryOperator<T> | (T, T) -> T | Представляет операцию, которая принимает на вход два элемента одного типа и возвращает значение того же типа. |
7 ответов
Три очевидных ситуации, когда это нужно:
- Вызов другого конструктора из того же класса, что и первая часть вашего конструктора
- Различение локальной переменной и переменной экземпляра (в конструкторе или любом другом методе)
- Передача ссылки на текущий объект другому методу
Вот пример всех трех:
Я полагаю, что есть также некоторые странные ситуации, использующие внутренние классы, где вам нужно , но их следует избегать, так как они очень туманны, IMO: )
РЕДАКТИРОВАТЬ: Я не могу придумать ни одного примера, почему вы захотите это для прямого вызова метода .
РЕДАКТИРОВАТЬ: saua внесла это в вопрос о неясных внутренних примерах:
Я использую «это» для пояснения кода, часто в качестве подсказки, что я вызываю метод экземпляра, а не обращаюсь к методу уровня класса или полю.
Но нет. Если устранение неоднозначности не требуется из-за коллизии именования области, на самом деле вам не нужно «this».
Для большинства общих программ ключевое слово является необязательным и обычно используется во избежание путаницы. Однако есть несколько мест, где это необходимо.
Это не все случаи, но некоторые из более общих. Надеюсь, это поможет вам лучше понять и ключевые слова и как /когда их использовать.
Единственный раз, когда это действительно требуется, — это когда у метода есть параметр с тем же именем, что и у переменной-члена. Лично я стараюсь всегда использовать это, чтобы сделать область действия переменной /метода явной. Например, у вас может быть статический метод или метод экземпляра. При чтении кода может быть полезно узнать, что есть что.
Не ответ (так что не стесняйтесь голосовать за него), но я не смог уместить это в комментарии, где кто-то спрашивал.
Многие люди используют this.x, чтобы визуально отличать переменные экземпляра от локальных переменных и параметров.
Так они и сделали бы:
Лично я считаю, что это плохая привычка: любой полезный редактор надёжно поместит для вас экземплярные и локальные переменные в другой цвет — для этого не требуются какие-либо человеческие ошибки.
Делать это с «этим». только 50% безопасно. Конечно, компилятор поймает его, если вы попытаетесь поместить this.x, когда x является локальной переменной, но ничто не помешает вам «забыть» пометить переменную экземпляра этим., И если вы забудете пометить только один (или если кто-то еще работает над вашим кодом), и вы полагаетесь на шаблон, тогда шаблон может быть скорее разрушительным, чем хорошим
Лично я вполне уверен, что закономерность проистекает из недовольства программистов (вполне законно) тем, что в данном случае:
тот факт, что вам нужно «это». передо мной определяется название параметра — я согласен, что он просто чувствует себя неряшливо. Вы хотите быть последовательным — если вам это нужно. передо мной, почему бы не всегда использовать его?
Хотя я понимаю дискомфорт, набираю это. каждое место, где используется переменная экземпляра, просто педантично, бессмысленно, безобразно и ненадежно. Если вас это действительно беспокоит, и вам абсолютно нужно использовать шаблон для его решения, попробуйте ввести «p» перед вашими параметрами. Как побочный эффект, он должен даже сделать его более постоянным, потому что регистр параметра теперь будет соответствовать регистру метода.
Единственная причина добавить перед вызовом метода — указать, что вы вызываете нестатический метод. Я не могу придумать какой-либо другой веской причины сделать это (поправьте меня, я ошибаюсь). Я не рекомендую это соглашение, поскольку оно не добавляет особой ценности. Если не применять последовательно, то это может ввести в заблуждение (поскольку вызов метода без этого все еще может быть нестатическим). Как часто заботится, является ли вызываемый метод статическим или нет? Кроме того, большинство IDE по-разному выделяют статические методы.
Я слышал о соглашениях, где указывает на вызов метода подкласса при отсутствии вызывает метод суперкласса. Но это просто глупо, так как конвенция может быть наоборот.
Изменить . Как отмечает mmyers (см. комментарий), работает со статическими методами. При этом я не вижу абсолютно никакой причины добавлять , поскольку это не имеет никакого значения.
Это абсолютно необходимо, если ваш метод должен вернуть экземпляр объекта.
Это позволяет связывать методы вместе следующим образом:
Two-Arity Function Specializations
To define lambdas with two arguments, we have to use additional interfaces that contain “Bi” keyword in their names: BiFunction, ToDoubleBiFunction, ToIntBiFunction, and ToLongBiFunction.
BiFunction has both arguments and a return type generified, while ToDoubleBiFunction and others allow you to return a primitive value.
One of the typical examples of using this interface in the standard API is in the Map.replaceAll method, which allows replacing all values in a map with some computed value.
Let’s use a BiFunction implementation that receives a key and an old value to calculate a new value for the salary and return it.
1.1 Объекты и классы
Сегодня вы узнаете немного о том, как устроена типичная программа на Java. И главная новость: каждая программа на Java состоит из классов и объектов.
Что такое классы, вы уже знаете, а что такое объекты?
Начну с аналогии. Представьте, что вы хотите сделать небольшой корабль. Сначала нужно сделать чертёж, затем отдать его на завод, где по этому чертежу соберут корабль. Или десяток. Да вообще, сколько угодно кораблей
По одному чертежу строятся десятки идентичных кораблей, вот что важно
В программировании на Java все точно так же.
Чертежи
Программист — он как проектировщик. Только проектировщик рисует чертежи, а Java-программист пишет классы. Затем на основе чертежей создаются детали, а на основе классов — объекты.
Сначала мы пишем классы (делаем чертежи), а потом, во время исполнения программы, на основе этих классов Java-машина создает объекты. Точно так же, как корабли создаются на основе чертежей.
Чертёж один, но кораблей может быть много. Корабли разные, у них разные имена, они возят разные грузы. Но они очень похожие: они все — корабли с идентичной конструкцией, и могут выполнять аналогичные задачи.
Или вот еще аналогия…
Муравейник
Муравейник — это хороший пример взаимодействия объектов. В простейшем муравейнике есть три класса муравьёв: королева, воины и рабочие муравьи.
Количество муравьёв каждого класса — разное. Королева — одна на весь муравейник, воинов — десятки, а рабочих муравьёв — сотни. Три класса и сотни объектов. Муравьи взаимодействуют друг с другом, с такими же муравьями и муравьями других классов по жёстко заданным правилам.
Это просто идеальный пример. В типичной программе все точно так же. Есть главный объект, который создаёт объекты всех остальных классов. Объекты начинают взаимодействовать друг с другом и «внешним миром» программы. Внутри этих объектов жёстко запрограммировано их поведение.
Два этих пояснения — это две стороны одной медали. Истина посередине. Первый пример (про чертеж и корабли) показывает связь между классом и объектами этого класса. Аналогия очень сильная. Второй пример (про муравейник) показывает связь между объектами, которые существуют во время работы программы, и написанными классами.
Сначала вы должны написать классы для всех существующих в программе объектов, а потом ещё и описать их взаимодействие. Да, это так, но это легче, чем кажется.
В Java все сущности во время работы программы являются объектами, а написание программы сводится к описанию различных способов взаимодействия объектов. Объекты просто вызывают методы друг друга и передают в них нужные данные.
Документация
А как узнать, какие данные передавать в методы? Тут все уже придумано до вас.
Обычно у каждого класса есть описание, в котором говорится, для чего он создан. Также обычно и у каждого публичного метода есть описание: что он делает, и какие данные нужно в него передавать.
Чтобы использовать класс, нужно в общих чертах знать, что он делает. А также нужно точно знать, что делает каждый его метод. И совсем не обязательно знать, как он это делает. Такая себе волшебная палочка.
Давайте посмотрим на код — копирование файла:
Копирование файла c:\data.txt в файл c:\result.txt |
---|
Если прочитать это код построчно, можно догадаться, что он делает в общих чертах. Хотя тут нужен опыт и практика. Так что спустя некоторое время этот код вам покажется знакомым и понятным.
Functional Interfaces
All functional interfaces are recommended to have an informative @FunctionalInterface annotation. This not only clearly communicates the purpose of this interface, but also allows a compiler to generate an error if the annotated interface does not satisfy the conditions.
Any interface with a SAM(Single Abstract Method) is a functional interface, and its implementation may be treated as lambda expressions.
Note that Java 8’s default methods are not abstract and do not count: a functional interface may still have multiple default methods. You can observe this by looking at the Function’s documentation.
Using this for Ambiguity Variable Names
In Java, it is not allowed to declare two or more variables having the same name inside a scope (class scope or method scope). However, instance variables and parameters may have the same name. For example,
In the above program, the instance variable and the parameter have the same name: age. Here, the Java compiler is confused due to name ambiguity.
In such a situation, we use this keyword. For example,
First, let’s see an example without using keyword:
Output:
mc.age = 0
In the above example, we have passed as a value to the constructor. However, we are getting as an output. This is because the Java compiler gets confused because of the ambiguity in names between instance the variable and the parameter.
Now, let’s rewrite the above code using keyword.
Output:
obj.age = 8
Now, we are getting the expected output. It is because when the constructor is called, inside the constructor is replaced by the object obj that has called the constructor. Hence the age variable is assigned value 8.
Also, if the name of the parameter and instance variable is different, the compiler automatically appends this keyword. For example, the code:
is equivalent to:
this with Getters and Setters
Another common use of keyword is in setters and getters methods of a class. For example:
Output:
obj.name: Toshiba
Here, we have used keyword:
- to assign value inside the setter method
- to access value inside the getter method
Lambdas in Java 8
Java 8 brought a powerful new syntactic improvement in the form of lambda expressions. A lambda is an anonymous function that can be handled as a first-class language citizen, for instance passed to or returned from a method.
Before Java 8, you would usually create a class for every case where you needed to encapsulate a single piece of functionality. This implied a lot of unnecessary boilerplate code to define something that served as a primitive function representation.
Lambdas, functional interfaces and best practices of working with them, in general, are described in the article “Lambda Expressions and Functional Interfaces: Tips and Best Practices”. This guide focuses on some particular functional interfaces that are present in the java.util.function package.
С этим читают
- Возможности javascript, о существовании которых я не знал
- Class yii\grid\actioncolumn
- Установка и настройка java в windows 7
- Как научиться программировать на java
- Управление bean-компонентами spring при конфигурировании на основе java
- Теория вероятностей на практике или знаете ли вы о random
- Java swing tutorial: examples to create gui
- Вспомогательные классы для работы со строками в java
- Java tostring() method
- Jar-файлы в windows