JavaRush /Java-Blog /Random-DE /Wie man Methoden effektiv schreibt (Übersetzung des Artik...
pena
Level 41
Москва

Wie man Methoden effektiv schreibt (Übersetzung des Artikels)

Veröffentlicht in der Gruppe Random-DE
Der Originalartikel ist unter: http://www.javacodegeeks.com/2015/09/how-to-write-methods-efficiently.html#download-Tutorial Veröffentlicht von: Andrey Redko (Andrey Redko) in Core Java (Java Core ) 18. September 2015 Diese Notiz ist Teil des Advanced Java-Kurses unserer Akademie. Dieser Kurs soll Ihnen dabei helfen, Java effektiver zu nutzen. Hier werden fortgeschrittenere Themen besprochen, z. B. Objekterstellung, Parallelisierung, Serialisierung, Reflexion und vieles mehr. Dieses Wissen wird Sie auf Ihrem Weg zu den Höhen der Java-Beherrschung begleiten.
Kursinhalte
1. Einführung 2. Methodensignatur 3. Methodenkörper 4. Methodenüberladung 5. Methodenüberschreibung 6. Inlining 7. Rekursion 8. Methodenreferenzen 9. Unveränderlichkeit 10. Methodendokumentation 11. Methodenparameter und Rückgabewerte 12. Methode als Einstiegspunkt zu Anhang 13. Was kommt als nächstes? 14. Herunterladen des Quellcodes
1. Einleitung
In diesem Abschnitt des Tutorials werden wir einige Zeit damit verbringen, verschiedene Aspekte im Zusammenhang mit dem Entwerfen und Implementieren von Methoden in Java zu diskutieren. Im vorherigen Teil des Tutorials haben Sie gesehen, dass das Schreiben von Methoden in Java sehr einfach ist, aber es gibt viele Dinge, die Ihre Methoden lesbarer und effizienter machen können.
2. Methodensignaturen
Wie Sie bereits wissen, ist Java eine objektorientierte Sprache. Im Wesentlichen gehört jede Java-Methode zu einem Teil einer Klasse (oder zur Klasse selbst im Fall einer statistischen Methode). Es verfügt über Sichtbarkeits- (oder Zugänglichkeits-)Regeln, kann als abstrakt oder endgültig deklariert werden und so weiter. Der vielleicht wichtigste Teil einer Methode ist jedoch ihre Signatur: der Rückgabetyp und die Argumente sowie die Liste der geprüften Ausnahmen, die ausgelöst werden können, in der Implementierung jeder Methode (dieser Teil wurde jedoch in der Vergangenheit nicht oft und heutzutage noch seltener verwendet). ). Beginnen wir mit einem kleinen Beispiel. 1 public static void main( String[] args ) { 2 // Some implementation here 3 } Die Hauptmethode akzeptiert ein String-Array nur als Argument und gibt nichts zurück. Es könnte sehr schön sein, alle Methoden so einfach wie main zu machen. In Wirklichkeit kann es jedoch sein, dass die Methodensignatur unleserlich wird. Schauen wir uns das folgende Beispiel an: 1 public void setTitleVisible( int lenght, String title, boolean visible ) { 2 // Some implementation here 3 } Das erste, was hier auffällt, ist, dass die Konventionen nativ in Java-Methodennamen verwendet werden, wie zum Beispiel setTitleVisible. Der Name ist gut gewählt und versucht zu beschreiben, was die Methode tun soll. Zweitens verraten die Namen der Argumente (oder geben zumindest einen Hinweis darauf), welchen Zweck sie haben. Es ist sehr wichtig, korrekte, aussagekräftige Namen für Methodenargumente zu finden, anstelle von int i, String s, boolean f (in sehr seltenen Fällen ist dies jedoch sinnvoll). Drittens hat die Methode nur drei Argumente. Obwohl in Java eine viel höhere Anzahl zulässiger Argumente gilt, wird dringend empfohlen, die Anzahl der Argumente nicht über 6 zu überschreiten. Wird diese Grenze überschritten, ist die Signatur schwer zu verstehen. Seit der Veröffentlichung von Java 5 können Methoden unterschiedliche Listen mit Argumenten desselben Typs (Varargs genannt) haben und eine spezielle Syntax verwenden, wie zum Beispiel: 1 public void find( String … elements ) { 2 // Some implementation here 3 } Intern konvertiert der Java-Compiler variable Argumente in ein Array der entsprechenden Typen und somit können variable Argumente verwendet werden übernommen, um die Methode umzusetzen. Interessanterweise können Sie in Java auch Varargs mithilfe generischer Parameter deklarieren. Da der Typ des Arguments jedoch unbekannt ist, möchte der Java-Compiler sicherstellen, dass Varargs korrekt verwendet werden, und empfiehlt, endgültige Methoden mit @SafeVarargs zu annotieren (weitere Informationen finden Sie in Teil 5 des Tutorials, Wie und wann zu verwenden). Aufzählungen und Anmerkungen. Wir verwenden Aufzählungen und Kommentare. Zum Beispiel: 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 } Следующий пример демонстрирует использование проверки исключений Wie части сигнатуры метода. В недалеком прошлом проверка исключений показала себя не настолько полезной, Wieой она предполагалась быть, в результате шаблонный Code был использован скорее для записи, чем для решения проблем. 1 public void write( File file ) throws IOException { 2 // Some implementation here 3 } Последнее, но, тем не менее, важное, Wie правило, рекомендуется (но редко используется), отметить аргументы метода, Wie final. Это поможет избавиться от практики написания плохого Codeа, когда аргументы метода предназначены различным значениям. Кроме того, такие аргументы метода могут быть использованы анонимными классами (подробнее об анонимных классов рассматривается в части 3 учебника, , How to design Classes and Interfaces (Как проектировать Классы и Интерфейсы)), хотя Java 8 облегчила немного это ограничение путем введения эффективных final переменных.
3. Тело метода
Каждый метод имеет свою реализацию и цель существования. Однако, имеется пара общих рекомендаций которые реально помогают написанию ясных и понятных методов. Вероятно, наиболее важный принцип - это принцип единичной ответственности: нужно пытаться реализовать метод таким путем, чтобы каждый единичный метод делал что-то одно, и делал это хорошо. Следуя этому принципу возможно раздувание количества методов класса, и важно найти правильный баланс. Другая важная вещь в процессе Codeирования и проектирования - это делать реализуемые методы короткими. Для коротких методов легко понять причину, по которой они сделаны, плюс они обычно умещаются на экран, и таким образом могут быть очень быстро поняты читателем вашего Codeа. Последний по порядку (но не по значению) совет связан с использованием return операторов. Если метод возвращает некоторое Bedeutung, пытайтесь минимизировать число мест, где return Bedeutung было бы вызвано (некоторые люди идут даже дальше и рекомендуют использовать лишь единичное return Bedeutung во всех случаях. Чем больше return значений имеет метод, тем более тяжело становится следовать его логике и модифицировать (oder оптимизировать) реализацию.
4. Перегрузка метода
Техника перегрузки методов часто используется, чтобы обеспечить специализацию версий метода для различных типов аргументов oder их комбинаций. Хотя Name метода одинаковое компьютер выбирает правильную альтернативу, углубляясь в текущие значения аргументов в точке вызова (лучший пример перегрузки это конструкторы Java: Name всегда одинаковое, но аргументы разные) oder вызывает ошибку компилятора, если такой вариант метода не найден. Например: 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 (Как и когда использовать дженерики)), однако перегрузка используется в случае, где подход с использованием дженериков не работает хорошо и каждый oder большинство типов аргументов, которые являются дженериками, требуют своих собственных специализированных реализаций. Тем не менее, комбинируя оба способа дженерики и перегрузку можно быть очень производительным, но часто это невозможно в 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 } Хотя этот кусок Codeа мог быть написан без использования дженериков, это неважно для наших демонстрационных целей. Интересно, что метод numberToString перегружен специальной реализацией BigDecimal и Ausführung на дженериках предназначена для всех остальных чисел.
5. Переопределение метода
Мы много говорoder о переопределении методов в части 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. Давайте создадим подкласс этого класса и попытаемся придумать версию метода преобразования чисел в строки (anstatt необработанных ein Objektов). 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 не выдает ниWieих ошибок и предупреждений. Теперь, давайте добавим еще один метод к дочернему классу. 1 public class Child extends Parent { 2 public String toObject( Double number ) { 3 return number.toString(); 4 } 5 } Опять же, есть только небольшая разница в сигнатуре метода (Double anstatt Number), но то, что в данном случае это перегруженная Ausführung метода, не отменяет переопределения метода родителя. То есть, когда подсказка от компилятора Java и @Override аннотации перекрываются: метод с аннотацией из последнего примера с @Override вызовет ошибку компилятора.
6. Встраивание
Встраивание - это оптимизация, осуществляемая с помощью Java JIT (точно в срок) компилятора для того, чтобы устранить конкретный вызов метода и заменить его непосредственно реализацией метода. Использование компилятора JIT эвристики зависит от двух вещей - Wie часто метод вызывается в настоящее время, а также от того, насколько он большой. Методы, которые слишком велики, не могут быть эффективно встроены. Встраивание может обеспечить значительный прирост производительности Codeа и преимущество хранения методов короткими, Wie мы уже обсуждали в разделе 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. Но не все так плохо, Wie кажется, потому что есть техника, которая может устранить переполнение стека, называемая 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. Основание, которое трактует методы Wie данные, понятие, которое не поддерживалось в языке до этого (однако, с тех пор Wie выпущена Java 7, JVM и стандартная библиотека Java уже были некоторые наработки, чтобы сделать это возможным). К счастью, имея ссылки методов, теперь это возможно. Ссылка статического метода: SomeClass::staticMethodName Ссылка на метод экземпляра конкретного ein Objektа: someInstance::instanceMethodName Ссылка на метод экземпляра произвольного ein Objektа определенного типа: SomeType::methodName Ссылка на конструктор: SomeClass::new Давайте взглянем на небольшой пример того, Wie методы могут быть использованы в качестве аргументов других методов. 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/). В ней говорится, очень ясно, что сеттеры могут изменить состояние ein Objektа, что- то до этого содержащего, и это то, что ожидает каждый разработчик Java. Тем не менее, альтернативный подход мог бы не менять состояние, а возвращать новый ein Objekt (new) каждый раз. Это не так страшно, Wie кажется, и новый Java 8 Date/Time API ( разработан под JSR 310: Date and Time API прикрытием) является отличным примером этого. Давайте взглянем на следующий фрагмент Codeа: 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 ein Objektа, который должен изменить свое состояние возвращает новый экземпляр LocalDateTime, и держит оригинал без изменений. Это большой сдвиг в парадигме дизайна API по сравнению с старыми Calendar и Date, (которые, мягко говоря, были не очень приятны в использовании и вызвали много головной боли).
10. Документирование метода
В Java, в частности, если вы разрабатываете Wieую-то библиотеку oder framework, все публичные методы должны быть заdokumentierenированы с помощью инструмента Javadoc (http://www.oracle.com/technetwork/articles/java/index-jsp-135444.html). Строго говоря, ничего не заставляет вас делать это, но хорошая dokumentierenация помогает другим разработчикам понять, что конкретный метод делает, Wieие аргументы он требует, Wieовы предположения oder ограничения его реализации, Wieие типы исключений он вызывает и когда они возникают, Wieое может быть возвращаемое Bedeutung (если таковые имеются), а также многие другие вещи. Давайте взглянем на следующий пример: 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 }
Это довольно многословная dokumentierenация для такого простого метода Wie parse, но это показывает пару полезных возможностей обеспечиваемых инструментом Javadoc tool, в том числе ссылки на другие классы, образцы фрагментов и продвинутого форматирования. Вот Wie этот dokumentierenация методов отражается в Eclipse, одной из популярных Java IDE. Просто глядя на изображение выше, любой разработчик Java от младшего до старшего уровня может понять цель метода и надлежащим образом использовать ее.
11. Параметры метода и возвращаемые значения
Документирование ваших методов - это великая вещь, но, к сожалению, это не предупреждает случаи, когда метод называют, используя неправильные oder неожиданные значения аргументов. Из-за этого, Wie правило, все публичные методы должны подтвердить свои аргументы и никогда не должны быть уверены, что все время при вызове будут указаны правильные значения (паттерн более известный Wie 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 операторы. Однако, те, которые могли быть выключены во время выполнения и могут быть не выполнены. Предпочтительно, всегда выполнять такие проверки и вызывать соответствующие исключения. Даже имея dokumentierenированные методы и проверку их аргументов, хочу сделать еще пару замечаний связанных с возвращаемыми значениями. До того Wie вышла Java 8, самым простым способом сказать что метод в данное время не имеет значения чтобы его возвратить было просто вернуть нуль. Вот почему Java так плохо получить исключение NullPointerException. Java 8 пытается решить этот вопрос с введением Optional < T > class. Давайте взглянем на этот пример: 1 public< T > Optional< T > find( String id ) { 2 // Some implementation here 3 } Optional < T > предоставляет много полезных методов, и völlig устраняет необходимость возвращать в методе null и загрязнять везде ваш Code проверками на null. Единственное исключение, вероятно, это коллекции. Всякий раз, когда метод возвращает коллекцию, всегда лучше вернуть null anstatt null (и даже Optional < T >), например: 1 public< T > Collection< T > find( String id ) { 2 return Collections.emptyList(); 3 }
12. Метод Wie точка входа в приложение
Selbst wenn Sie ein einfacher Entwickler sind, der Anwendungen in Ihrem Unternehmen schreibt, oder an einem der beliebtesten Java-Frameworks oder -Bibliotheken mitwirken, spielen die von Ihnen getroffenen Designentscheidungen eine sehr wichtige Rolle bei der Verwendung Ihres Codes. Während API-Designrichtlinien mehrere Bücher wert sind, behandelt dieser Teil des Tutorials viele davon (wie Methoden zum Einstiegspunkt einer API werden). Daher ist ein kurzer Überblick sehr hilfreich: • Verwenden Sie aussagekräftige Namen für Methoden und ihre Argumente (Methode Signaturen) Versuchen Sie, die Anzahl der Argumente auf weniger als 6 zu beschränken (Abschnitt „Methodensignaturen“) • Halten Sie Ihre Methoden kurz und lesbar (Abschnitt „Methodenkörper und Inlining“) • Dokumentieren Sie immer öffentliche Methoden, einschließlich Vorbedingungen und Beispielen, wenn es sinnvoll ist (Abschnitt „Methode“) Dokumentation) • Führen Sie stets Argumentprüfungen und Plausibilitätsprüfungen durch (Abschnitt Methodenparameter und Rückgabewerte). • Versuchen Sie, Nullen als Rückgabewerte zu vermeiden (Abschnitt Methodenparameter und Rückgabewerte). • Versuchen Sie, wann immer es sinnvoll ist, unveränderliche Methoden zu entwerfen (was auch der Fall ist). wirkt sich nicht auf den internen Zustand aus, Abschnitt „Unveränderlichkeit“) • Verwenden Sie Sichtbarkeits- und Zugänglichkeitsregeln, um Methoden auszublenden, die nicht öffentlich sein sollten (Teil 3 des Tutorials, So entwerfen Sie Klassen und Schnittstellen)
13. Was kommt als nächstes?
In diesem Teil des Tutorials geht es etwas weniger um Java als Sprache, sondern mehr um die effektive Nutzung der Java-Sprache, insbesondere um das Schreiben lesbarer, sauberer, dokumentierter und effizienter Methoden. Im nächsten Abschnitt werden wir dieselbe Grundidee fortsetzen und allgemeine Programmierprinzipien diskutieren, die Ihnen helfen sollen, ein besserer Java-Entwickler zu werden.
14. Laden Sie den Quellcode herunter
In dieser Lektion ging es darum, wie man Methoden effektiv schreibt. Den Quellcode können Sie hier herunterladen:
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION