JDK 7: Диамантеният оператор

Project Coin предоставя множество „малки езикови подобрения“ като подмножество на новите функции на JDK 7. Наскоро публикувах блог за превключването на Strings на Project Coin и в този пост пиша за новия Diamond Operator ( ).

Diamond Operator намалява някои от многословието на Java, обграждащи генеричните продукти, като компилаторът извежда типове параметри за конструктори на общи класове. Оригиналното предложение за добавяне на Diamond Operator към езика Java е направено през февруари 2009 г. и включва този прост пример:

Например, помислете за следния израз на възлагане:

Карта anagrams = нов HashMap ();

Това е доста дълго, така че може да бъде заменено с това:

Карта анаграми = нов HashMap ();

Горният пример, предоставен в предложението на Джеръми Менсън (който беше един от първите в отговор на покана за идеи за проектни монети), е прост, но адекватно демонстрира как се прилага Diamond Operator в JDK 7. Предложението на Менсън също дава съществено значение защо това допълнение беше желателно:

Изискването параметрите на типа да се дублират ненужно харесва

това насърчава един нещастник

изобилие от статични фабрични методи, просто защото заключението на типа

работи по извиквания на методи.

С други думи, JDK 7 Project Coin добавянето на диамантен оператор носи извод за тип на конструктори, който е бил достъпен с методи. С методите умозаключването се прави имплицитно, когато се остави изричната спецификация на типа параметри. С екземпляра, от друга страна, диамантеният оператор трябва да бъде посочен изрично, за да "каже" на компилатора да изведе типа.

В оригиналното си предложение Менсън посочва, че синтаксисът без специален диамантен оператор не може да се използва за имплицитно извеждане на типове за инстанции, защото „за целите на обратната съвместимост new Map () посочва суров тип и следователно не може да се използва за тип умозаключение. " Страницата за извеждане на типове в урока за генерични изследвания на изучаването на езика на Java на уроците за Java включва раздел, наречен „Извеждане на типове и инстанциране на родови класове“, който вече е актуализиран, за да отрази Java SE 7. Този раздел описва също защо специалният операторът трябва да бъде посочен, за да информира изрично компилатора да използва извод за тип при инстанциране:

Имайте предвид, че за да се възползвате от автоматичното извеждане на типа по време на създаването на общ клас, трябва да посочите диамантения оператор. В следващия пример компилаторът генерира непроверено предупреждение за преобразуване, защото конструкторът HashMap () се позовава на суров тип HashMap, а не на Map Тип

В точка 24 („Премахване на непроверени предупреждения“) от Второто издание на Ефективна Java, Джош Блок подчертава с получер текст „Премахнете всяко непроверено предупреждение, което можете“. Bloch показва пример за непроверено предупреждение за преобразуване, което се появява, когато се компилира код, който използва суров тип от дясната страна на декларацията. Следващият списък с кодове показва код, който ще доведе до това предупреждение.

final Map
     
       statesToCities = new HashMap(); // raw! 
     

Следващите две екранни снимки показват отговора на компилатора на горния ред код. Първото изображение показва съобщението, когато няма активирани предупреждения -Xlint, а второто показва по-изричното предупреждение, което се появява, когато -Xlint:uncheckedе предоставено като аргумент на javac.

Ако е ефективна Java , Bloch посочва, че това конкретно непроверено предупреждение е лесно да се адресира чрез изрично предоставяне на типа параметър на инстанцията на родовия клас. С JDK 7 това ще бъде още по-лесно! Вместо да се налага да добавяте изричен текст с тези имена на типове, типовете могат да бъдат изведени в много случаи и спецификацията на диамантения оператор казва на компилатора да направи това заключение, вместо да използва суров тип.

Следващият списък с кодове на Java предоставя опростени примери за тези концепции. Съществуват методи, които демонстрират създаване на екземпляр на суров набор, създаване на екземпляр на набор с изрична спецификация на неговия тип параметър и създаване на екземпляр на набор с тип параметри, изведен поради спецификация на диамантения оператор ( ).

package dustin.examples; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import static java.lang.System.out; /** * Very simple demonstration of JDK 7's/Project Coin's "Diamond Operator." */ public class DiamondOperatorDemo { /** Use of "raw" type. */ private static Set rawWithoutExplicitTyping() { final Set names = new HashSet(); addNames(names); return names; } /** Explicitly specifying generic class's instantiation parameter type. */ private static Set explicitTypingExplicitlySpecified() { final Set names = new HashSet(); addNames(names); return names; } /** * Inferring generic class's instantiation parameter type with JDK 7's * 'Diamond Operator.' */ private static Set explicitTypingInferredWithDiamond() { final Set names = new HashSet(); addNames(names); return names; } private static void addNames(final Set namesToAddTo) { namesToAddTo.add("Dustin"); namesToAddTo.add("Rett"); namesToAddTo.add("Homer"); } /** * Main executable function. */ public static void main(final String[] arguments) { out.println(rawWithoutExplicitTyping()); out.println(explicitTypingExplicitlySpecified()); out.println(explicitTypingInferredWithDiamond()); } } 

Когато горният код е компилиран, само "суровият" случай води до предупреждение.

На този етап може да бъде проницателно да разгледаме какво ни казва javap за тези три метода. Това се прави в този случай с командата ( -vопцията за подробно дава всички сочни подробности и -pпоказва тези сочни подробности за privateметодите):

javap -v -p -classpath classes dustin.examples.DiamondOperatorDemo 

Тъй като всички тези методи са били в един клас, има един поток изход за целия клас. За да улесня обаче тяхното сравнение, изрязах и поставих изхода във формат, който подравнява изхода на javap за всеки метод един срещу друг. Всяка колона представлява javapизхода за един от методите. Промених цвета на шрифта на конкретния метод на син, за да се открои и да обознача изхода на тази колона.

Освен имената на самите методи, няма разлика в javapизхода. Това е така, защото изтриването на родовия тип Java означава, че диференциацията въз основа на типа не е налична по време на изпълнение. Урокът за Java за Generics включва страница, наречена Изтриване на тип, която обяснява това:

Компилаторът премахва цялата информация за действителния аргумент на типа по време на компилация.

Съществува изтриване на типа, така че новият код може да продължи да взаимодейства със стария код. Използването на суров тип по някаква друга причина се счита за лоша практика при програмиране и трябва да се избягва, когато е възможно.

Както ни напомня цитатът по-горе, изтриването означава, че байт кодирането на суров тип не се различава от изрично въведения тип параметър, но също така насърчава разработчиците да не използват сурови типове, с изключение на интегрирането с наследения код.

Заключение

Включването на диамантения оператор ( ) в Java SE 7 означава, че кодът, който създава екземпляри на общи класове, може да бъде по-малко подробен. Езиците за кодиране като цяло и Java в частност се придвижват към идеи като конвенция за конфигурация, конфигурация по изключение и извеждане на нещата възможно най-често, вместо да изискват изрична спецификация. Динамично типизираните езици са добре известни със заключението за тип, но дори статично типизираната Java може да направи повече от това, отколкото прави, а диамантеният оператор е пример за това.

Оригинално публикуване на разположение на //marxsoftware.blogspot.com/

Тази история „JDK 7: Диамантеният оператор“ първоначално е публикувана от JavaWorld.