JavaRush /Blog Java /Random-PL /Jak skutecznie pisać metody (tłumaczenie artykułu)
pena
Poziom 41
Москва

Jak skutecznie pisać metody (tłumaczenie artykułu)

Opublikowano w grupie Random-PL
Oryginalny artykuł znajduje się pod adresem: http://www.javacodegeeks.com/2015/09/how-to-write-methods-efektywnie.html#download tutorial Opublikował: Andrey Redko (Andrey Redko) w Core Java (Java Core) 18 września 2015 Ta notatka jest częścią kursu Advanced Java naszej akademii, który ma pomóc Ci w efektywniejszym korzystaniu z Java. Omówiono tutaj bardziej zaawansowane tematy, takie jak tworzenie obiektów, równoległość, serializacja, refleksja i wiele innych. Ta wiedza poprowadzi Cię w podróż na wyżyny mistrzostwa w Javie.
Treść kursu
1. Wprowadzenie 2. Sygnatura metody 3. Treść metody 4. Przeciążanie metody 5. Nadpisywanie metody 6. Inlineing 7. Rekurencja 8. Referencje do metod 9. Niezmienność 10. Dokumentacja metody 11. Parametry metody i zwracane wartości 12. Metoda jako punkt wejścia do Załącznika 13. Co dalej 14. Pobieranie kodu źródłowego
1. Wstęp
W tej części tutoriala poświęcimy trochę czasu na omówienie różnych aspektów związanych z projektowaniem i wdrażaniem metod w Javie. W poprzedniej części tutoriala widziałeś, że pisanie metod w Javie jest bardzo proste, jednak jest wiele rzeczy, które mogą sprawić, że Twoje metody będą bardziej czytelne i wydajne.
2. Podpisy metod
Jak już wiesz, Java jest językiem obiektowym. Zasadniczo każda metoda Java należy do jakiejś części klasy (lub do samej klasy w przypadku metody statystycznej). Ma zasady widoczności (lub dostępności), może być uznany za abstrakcyjny lub ostateczny i tak dalej. Jednak być może najważniejszą częścią metody jest jej sygnatura: typ zwracany i argumenty, a także lista sprawdzonych wyjątków, które można zgłosić w każdej implementacji metody (ale ta część nie była często używana w przeszłości, a obecnie jest jeszcze rzadziej ). Zacznijmy od małego przykładu. 1 public static void main( String[] args ) { 2 // Some implementation here 3 } Metoda main przyjmuje tablicę ciągów znaków tylko jako argument args i nic nie zwraca. Byłoby bardzo miło, gdyby wszystkie metody były tak proste jak main. Ale w rzeczywistości podpis metody może stać się nieczytelny. Spójrzmy na następujący przykład: 1 public void setTitleVisible( int lenght, String title, boolean visible ) { 2 // Some implementation here 3 } Pierwszą rzeczą, którą tutaj zauważasz, jest to, że konwencje są natywnie używane w nazwach metod Java, np. setTitleVisible.Nazwa jest dobrze dobrana i próbuje opisać, co dana metoda ma robić. Po drugie, nazwy argumentów mówią (lub przynajmniej wskazują), jaki jest ich cel. Bardzo ważne jest znalezienie poprawnych, znaczących nazw argumentów metod zamiast int i, String s, boolean f (w bardzo rzadkich przypadkach ma to jednak sens). Po trzecie, metoda ma tylko trzy argumenty. Chociaż w Java obowiązuje znacznie wyższy limit liczby dozwolonych argumentów, zdecydowanie zaleca się, aby nie przekraczać liczby argumentów większej niż 6. Przekroczenie tego limitu powoduje, że podpis jest trudny do zrozumienia. Od czasu wydania Java 5 metody mogą mieć różne listy argumentów tego samego typu (tzw. varargs) i używać specjalnej składni, na przykład: 1 public void find( String … elements ) { 2 // Some implementation here 3 } Wewnętrznie kompilator Java konwertuje zmienne argumenty na tablicę odpowiednich typów, dzięki czemu zmienne argumenty mogą być przyjęte do wdrożenia metody. Co ciekawe, Java umożliwia także deklarowanie zmiennych przy użyciu parametrów ogólnych. Ponieważ jednak typ argumentu jest nieznany, kompilator Java chce mieć pewność, że zmienne varargs są używane poprawnie i zaleca, aby ostateczne metody były opatrzone adnotacją @SafeVarargs (więcej informacji można znaleźć w części 5 samouczka, Jak i kiedy używać Wyliczenia i adnotacje). Używamy wyliczeń i komentarzy). Na przykład: 1 @SafeVarargs 2 final public< T > void find( T ... elements ) { 3 // Some implementation here 4 } Другой ближайший путь это использовать @SuppressWarnings комментарии, например 1 @SuppressWarnings( "unchecked" ) 2 public< T > void findSuppressed( T ... elements ) { 3 // Some implementation here 4 } Следующий пример демонстрирует использование проверки исключений Jak части сигнатуры метода. В недалеком прошлом проверка исключений показала себя не настолько полезной, Jakой она предполагалась быть, в результате шаблонный kod был использован скорее для записи, чем для решения проблем. 1 public void write( File file ) throws IOException { 2 // Some implementation here 3 } Последнее, но, тем не менее, важное, Jak правило, рекомендуется (но редко используется), отметить аргументы метода, Jak final. Это поможет избавиться от практики написания плохого kodа, когда аргументы метода предназначены различным значениям. Кроме того, такие аргументы метода могут быть использованы анонимными классами (подробнее об анонимных классов рассматривается в части 3 учебника, , How to design Classes and Interfaces (Как проектировать Классы и Интерфейсы)), хотя Java 8 облегчила немного это ограничение путем введения эффективных final переменных.
3. Тело метода
Каждый метод имеет свою реализацию и цель существования. Однако, имеется пара общих рекомендаций которые реально помогают написанию ясных и понятных методов. Вероятно, наиболее важный принцип - это принцип единичной ответственности: нужно пытаться реализовать метод таким путем, чтобы каждый единичный метод делал что-то одно, и делал это хорошо. Следуя этому принципу возможно раздувание количества методов класса, и важно найти правильный баланс. Другая важная вещь в процессе kodирования и проектирования - это делать реализуемые методы короткими. Для коротких методов легко понять причину, по которой они сделаны, плюс они обычно умещаются на экран, и таким образом могут быть очень быстро поняты читателем вашего kodа. Последний по порядку (но не по значению) совет связан с использованием return операторов. Если метод возвращает некоторое oznaczający, пытайтесь минимизировать число мест, где return oznaczający было бы вызвано (некоторые люди идут даже дальше и рекомендуют использовать лишь единичное return oznaczający во всех случаях. Чем больше return значений имеет метод, тем более тяжело становится следовать его логике и модифицировать (Lub оптимизировать) реализацию.
4. Перегрузка метода
Техника перегрузки методов часто используется, чтобы обеспечить специализацию версий метода для различных типов аргументов Lub их комбинаций. Хотя Nazwa метода одинаковое компьютер выбирает правильную альтернативу, углубляясь в текущие значения аргументов в точке вызова (лучший пример перегрузки это конструкторы Java: Nazwa всегда одинаковое, но аргументы разные) Lub вызывает ошибку компилятора, если такой вариант метода не найден. Например: 1 public String numberToString( Long number ) { 2 return Long.toString( number ); 3 } 4 5 public String numberToString( BigDecimal number ) { 6 return number.toString(); 7 } Перегрузка метода отчасти близка к дженерикам (больше информации о дженериках можно найти в части 4 учебника How and when to use Generics (Как и когда использовать дженерики)), однако перегрузка используется в случае, где подход с использованием дженериков не работает хорошо и каждый Lub большинство типов аргументов, которые являются дженериками, требуют своих собственных специализированных реализаций. Тем не менее, комбинируя оба способа дженерики и перегрузку можно быть очень производительным, но часто это невозможно в Java, потому что тип стирается (больше информации в части 4 учебника How and when to use Generics (Как и когда использовать дженерики)). Давайте взглянем на пример: 1 public< T extends Number > String numberToString( T number ) { 2 return number.toString(); 3 } 4 5 public String numberToString( BigDecimal number ) { 6 return number.toPlainString(); 7 } Хотя этот кусок kodа мог быть написан без использования дженериков, это неважно для наших демонстрационных целей. Интересно, что метод numberToString перегружен специальной реализацией BigDecimal и wersja на дженериках предназначена для всех остальных чисел.
5. Переопределение метода
Мы много говорLub о переопределении методов в части 3 учебника (How to design Classes and Interfaces (Как проектировать классы и интерфейсы). В этом разделе, когда мы уже знаем о перегрузке методов, мы собираемся показать, почему использование @Override аннотации так важно. Наш пример продемонстрирует тонкое различие между переопределением метода и перегрузкой метода в простой иерархии классов. 1 public class Parent { 2 public Object toObject( Number number ) { 3 return number.toString(); 4 } 5 } Родительский класс имеет только один метод toObject. Давайте создадим подкласс этого класса и попытаемся придумать версию метода преобразования чисел в строки (zamiast необработанных obiektов). 1 public class Child extends Parent { 2 @Override 3 public String toObject( Number number ) { 4 return number.toString(); 5 } 6 } Тем не менее, сигнатура метода toObject в дочернем классе немногим отличается (см Covariant method return types (Ковариантные типы возвращаемые методами) для более подробной информации), и это делает переопределение его из суперкласса в свой класс, при этом компилятор Java не выдает ниJakих ошибок и предупреждений. Теперь, давайте добавим еще один метод к дочернему классу. 1 public class Child extends Parent { 2 public String toObject( Double number ) { 3 return number.toString(); 4 } 5 } Опять же, есть только небольшая разница в сигнатуре метода (Double zamiast Number), но то, что в данном случае это перегруженная wersja метода, не отменяет переопределения метода родителя. То есть, когда подсказка от компилятора Java и @Override аннотации перекрываются: метод с аннотацией из последнего примера с @Override вызовет ошибку компилятора.
6. Встраивание
Встраивание - это оптимизация, осуществляемая с помощью Java JIT (точно в срок) компилятора для того, чтобы устранить конкретный вызов метода и заменить его непосредственно реализацией метода. Использование компилятора JIT эвристики зависит от двух вещей - Jak часто метод вызывается в настоящее время, а также от того, насколько он большой. Методы, которые слишком велики, не могут быть эффективно встроены. Встраивание может обеспечить значительный прирост производительности kodа и преимущество хранения методов короткими, Jak мы уже обсуждали в разделе Method body (Тело метода).
7. Рекурсия
Рекурсия в Java - это техника, где метод вызывает сам себя, выполняя расчеты. Например, давайте взглянем на следующий пример, который суммирует число массива: 1 public int sum( int[] numbers ) { 2 if( numbers.length == 0 ) { 3 return 0; 4 } if( numbers.length == 1 ) { 5 return numbers[ 0 ]; 6 } else { 7 return numbers[ 0 ] + sum( Arrays.copyOfRange( numbers, 1, numbers.length ) ); 8 } 9 } Это очень неэффективная реализация, однако она демонстрирует рекурсию достаточно хорошо. Существует одна хорошо известная проблема с рекурсивными методами: в зависимости, насколько глубока цепь вызовов, они могут переполнить стек и вызвать исключение StackOverflowError. Но не все так плохо, Jak кажется, потому что есть техника, которая может устранить переполнение стека, называемая tail call optimization (оптимизация хвоста вызова). Она может быть применена, если метод с хвостовой рекурсией (методы с хвостовой рекурсией это методы, в которых все рекурсивные вызовы это хвостовые вызовы). Например, давайте перепишем предыдущий алгоритм с использованием в хвостовой рекурсии: 01 public int sum( int initial, int[] numbers ) { 02 if( numbers.length == 0 ) { 03 return initial; 04 } if( numbers.length == 1 ) { 05 return initial + numbers[ 0 ]; 06 } else { 07 return sum( initial + numbers[ 0 ], 08 Arrays.copyOfRange( numbers, 1, numbers.length ) ); 09 } 10 } К сожалению, на данный момент компилятор Java (а также компилятор JVM JIT) не поддерживает tail call optimization хвостовую оптимизация, но все-таки это очень полезная техника, и ее надо знать и принимать во внимание, когда вы пишете рекурсивные алгоритмы в Java.
8. Ссылки методов
В Java 8 сделан огромный шаг вперед, путем введения функциональных понятий в язык Java. Основание, которое трактует методы Jak данные, понятие, которое не поддерживалось в языке до этого (однако, с тех пор Jak выпущена Java 7, JVM и стандартная библиотека Java уже были некоторые наработки, чтобы сделать это возможным). К счастью, имея ссылки методов, теперь это возможно. Ссылка статического метода: SomeClass::staticMethodName Ссылка на метод экземпляра конкретного obiektа: someInstance::instanceMethodName Ссылка на метод экземпляра произвольного obiektа определенного типа: SomeType::methodName Ссылка на конструктор: SomeClass::new Давайте взглянем на небольшой пример того, Jak методы могут быть использованы в качестве аргументов других методов. 01 public class MethodReference { 02 public static void println( String s ) { 03 System.out.println( s ); 04 } 05 06 public static void main( String[] args ) { 07 final Collection< String > strings = Arrays.asList( "s1", "s2", "s3" ); 08 strings.stream().forEach( MethodReference::println ); 09 } 10 } В последней строке main метод использует ссылку на println метод чтобы напечатать каждый элемент из коллекции строк в консоль, он передается в качестве аргумента другому методу, forEach.
9. Неизменность
Неизменность обращает на себя много внимания в эти дни, и Java не является исключением. Хорошо известно, что неизменности трудно добиться в Java, но это не значит, что это должно быть проигнорировано. В Java, неизменность - это все знания об изменении внутреннего состояния. В качестве примера, давайте взглянем на спецификации JavaBeans (http://docs.oracle.com/javase/tutorial/javabeans/). В ней говорится, очень ясно, что сеттеры могут изменить состояние obiektа, что- то до этого содержащего, и это то, что ожидает каждый разработчик Java. Тем не менее, альтернативный подход мог бы не менять состояние, а возвращать новый obiekt (new) каждый раз. Это не так страшно, Jak кажется, и новый Java 8 Date/Time API ( разработан под JSR 310: Date and Time API прикрытием) является отличным примером этого. Давайте взглянем на следующий фрагмент kodа: 1 final LocalDateTime now = LocalDateTime.now(); 2 final LocalDateTime tomorrow = now.plusHours( 24 ); 3 4 final LocalDateTime midnight = now 5 .withHour( 0 ) 6 .withMinute( 0 ) 7 .withSecond( 0 ) 8 .withNano( 0 ); Каждый вызов LocalDateTime obiektа, который должен изменить свое состояние возвращает новый экземпляр LocalDateTime, и держит оригинал без изменений. Это большой сдвиг в парадигме дизайна API по сравнению с старыми Calendar и Date, (которые, мягко говоря, были не очень приятны в использовании и вызвали много головной боли).
10. Документирование метода
В Java, в частности, если вы разрабатываете Jakую-то библиотеку Lub framework, все публичные методы должны быть заdokumentированы с помощью инструмента Javadoc (http://www.oracle.com/technetwork/articles/java/index-jsp-135444.html). Строго говоря, ничего не заставляет вас делать это, но хорошая dokumentация помогает другим разработчикам понять, что конкретный метод делает, Jakие аргументы он требует, Jakовы предположения Lub ограничения его реализации, Jakие типы исключений он вызывает и когда они возникают, Jakое может быть возвращаемое oznaczający (если таковые имеются), а также многие другие вещи. Давайте взглянем на следующий пример: 01 /** 02 * The method parses the string argument as a signed decimal integer. 03 * The characters in the string must all be decimal digits, except 04 * that the first character may be a minus sign {@code '-'} or plus 05 * sign {@code '+'}. 06 * 07 *

An exception of type {@code NumberFormatException} is thrown if 08 * string is {@code null} or has length of zero. 09 * 10 *

Examples: 11 *

12	 * parse( "0" ) returns 0
13	 * parse( "+42") returns 42
14	 * parse( "-2" ) returns -2
15	 * parse( "string" ) throws a NumberFormatException
16	 * 
17 * 18 * @param str a {@code String} containing the {@code int} representation to be parsed 19 * @return the integer value represented by the string 20 * @exception NumberFormatException if the string does not contain a valid integer value 21 */ 22 public int parse( String str ) throws NumberFormatException { 23 return Integer.parseInt( str ); 24 }
Это довольно многословная dokumentация для такого простого метода Jak parse, но это показывает пару полезных возможностей обеспечиваемых инструментом Javadoc tool, в том числе ссылки на другие классы, образцы фрагментов и продвинутого форматирования. Вот Jak этот dokumentация методов отражается в Eclipse, одной из популярных Java IDE. Просто глядя на изображение выше, любой разработчик Java от младшего до старшего уровня может понять цель метода и надлежащим образом использовать ее.
11. Параметры метода и возвращаемые значения
Документирование ваших методов - это великая вещь, но, к сожалению, это не предупреждает случаи, когда метод называют, используя неправильные Lub неожиданные значения аргументов. Из-за этого, Jak правило, все публичные методы должны подтвердить свои аргументы и никогда не должны быть уверены, что все время при вызове будут указаны правильные значения (паттерн более известный Jak sanity checks (санитарная проверка)). Возвращаясь к нашему примеру из предыдущего раздела, метод parse должен выполнить проверку своего единственного аргумента, прежде чем делать что-нибудь с ним: 1 public int parse( String str ) throws NumberFormatException { 2 if( str == null ) { 3 throw new IllegalArgumentException( "String should not be null" ); 4 } 5 6 return Integer.parseInt( str ); 7 } Java имеет другой вариант выполнения проверки и sanity checks, используя assert операторы. Однако, те, которые могли быть выключены во время выполнения и могут быть не выполнены. Предпочтительно, всегда выполнять такие проверки и вызывать соответствующие исключения. Даже имея dokumentированные методы и проверку их аргументов, хочу сделать еще пару замечаний связанных с возвращаемыми значениями. До того Jak вышла Java 8, самым простым способом сказать что метод в данное время не имеет значения чтобы его возвратить было просто вернуть нуль. Вот почему Java так плохо получить исключение NullPointerException. Java 8 пытается решить этот вопрос с введением Optional < T > class. Давайте взглянем на этот пример: 1 public< T > Optional< T > find( String id ) { 2 // Some implementation here 3 } Optional < T > предоставляет много полезных методов, и w pełni устраняет необходимость возвращать в методе null и загрязнять везде ваш kod проверками на null. Единственное исключение, вероятно, это коллекции. Всякий раз, когда метод возвращает коллекцию, всегда лучше вернуть null zamiast null (и даже Optional < T >), например: 1 public< T > Collection< T > find( String id ) { 2 return Collections.emptyList(); 3 }
12. Метод Jak точка входа в приложение
Nawet jeśli jesteś prostym programistą piszącym aplikacje w swojej organizacji lub współpracownikiem jednego z najpopularniejszych frameworków lub bibliotek Java, podejmowane przez Ciebie decyzje projektowe odgrywają bardzo ważną rolę w sposobie wykorzystania Twojego kodu. Chociaż wytyczne dotyczące projektowania API są warte kilku książek, ta część samouczka omawia wiele z nich (w jaki sposób metody stają się punktem wejścia do API), więc szybki przegląd będzie bardzo pomocny: • Używaj znaczących nazw metod i ich argumentów (Metoda podpisy ) Staraj się, aby liczba argumentów była mniejsza niż 6 (sekcja Podpisy metod) • Staraj się, aby Twoje metody były krótkie i czytelne (sekcja Treść metody i Sekcja Wstawianie) • Zawsze dokumentuj metody publiczne, łącznie z warunkami wstępnymi i przykładami, jeśli ma to sens (sekcja Metoda Dokumentacja) • Zawsze wykonuj sprawdzanie argumentów i poprawności (sekcja Parametry metod i wartości zwracane) • Staraj się unikać wartości null jako wartości zwracanych (sekcja Parametry metod i wartości zwracane) • Kiedykolwiek ma to sens, próbuj projektować metody niezmienne (które nie nie wpływa na stan wewnętrzny, sekcja Niezmienność) • Użyj reguł widoczności i dostępności, aby ukryć metody, które nie powinny być publiczne (część 3 samouczka, Jak projektować klasy i interfejsy)
13. Co dalej
Ta część samouczka mówi mniej o Javie jako języku, a więcej o tym, jak efektywnie używać języka Java, w szczególności o pisaniu czytelnych, czystych, udokumentowanych i wydajnych metod. W następnej sekcji będziemy kontynuować tę samą podstawową ideę i omówimy ogólne zasady programowania, które mają pomóc Ci stać się lepszym programistą Java.
14. Pobierz kod źródłowy
Ta lekcja dotyczyła efektywnego pisania metod. Kod źródłowy możesz pobrać tutaj:
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION