Каков хороший вариант использования для статического импорта методов?

Только что получил комментарий, что мой статический импорт метода не был хорошей идеей. Статический импорт был методом из класса DA, который имеет в основном статические методы. Итак, в середине бизнес-логики у меня была активность да, которая, казалось, принадлежала текущему классу:

import static some.package.DA.*;
class BusinessObject {
  void someMethod() {
    ....
    save(this);
  }
} 

Рецензент не был заинтересован в том, чтобы я изменил код, и я этого не сделал, но я с ним согласен. Одна из причин, по которой не был выполнен статический импорт, заключалась в том, что метод определения путаницы был запутанным, его не было в текущем классе и ни в каком суперклассе, поэтому слишком долго нужно было определить его определение (система просмотра через веб не имеет кликабельности). ссылки вроде IDE:-) Я не думаю, что это имеет значение, статический импорт все еще довольно новый, и скоро мы все привыкнем к их поиску.

Но другая причина, с которой я согласен, заключается в том, что неквалифицированный вызов метода, кажется, принадлежит текущему объекту и не должен перескакивать контекстами. Но если бы это действительно принадлежало, имело бы смысл расширить этот суперкласс.

Итак, когда имеет смысл использовать статические методы импорта? Когда ты это сделал? Вам понравилось, как выглядят неквалифицированные звонки?

РЕДАКТИРОВАТЬ: популярное мнение, кажется, что методы статического импорта, если никто не собирается путать их как методы текущего класса. Например, методы из java.lang.Math и java.awt.Color. Но если abs и getAlpha не являются двусмысленными, я не понимаю, почему readEmployee. Как и во многих вариантах программирования, я думаю, что это тоже личное предпочтение.

Спасибо за ваш ответ, ребята, я закрываю вопрос.

16 ответов

Решение

Это из руководства Sun, когда они выпустили эту функцию (выделено в оригинале):

Итак, когда вы должны использовать статический импорт? Очень экономно! Используйте его только тогда, когда у вас возникнет искушение объявить локальные копии констант или злоупотреблять наследованием (Антипаттерн Constant Interface).... Если вы чрезмерно используете функцию статического импорта, она может сделать вашу программу нечитаемой и не поддерживаемой, загрязняя ее пространство имен всеми импортируемыми вами статическими элементами. Читатели вашего кода (включая вас через несколько месяцев после того, как вы его написали) не будут знать, из какого класса происходит статический член. Импорт всех статических членов из класса может быть особенно вредным для читабельности; если вам нужен только один или два участника, импортируйте их по отдельности.

( https://docs.oracle.com/javase/8/docs/technotes/guides/language/static-import.html)

Я хочу особо выделить две части:

  • Используйте статический импорт только тогда, когда у вас возникло искушение "злоупотребить наследованием". В этом случае вы бы испытали желание иметь BusinessObject extend some.package.DA? Если так, статический импорт может быть более чистым способом справиться с этим. Если бы вы никогда не мечтали о продлении some.package.DAтогда это, вероятно, плохое использование статического импорта. Не используйте его только для сохранения нескольких символов при наборе текста.
  • Импорт отдельных членов. Сказать import static some.package.DA.save вместо DA.*, Это значительно облегчит поиск источника импортированного метода.

Лично я использовал эту языковую функцию очень редко, и почти всегда только с константами или перечислениями, никогда с методами. Для меня компромисс почти никогда не стоит.

Другое разумное использование для статического импорта - JUnit 4. В более ранних версиях методов JUnit, таких как assertEquals а также fail были унаследованы после расширения тестового класса junit.framework.TestCase,

// old way
import junit.framework.TestCase;

public class MyTestClass extends TestCase {
    public void myMethodTest() {
        assertEquals("foo", "bar");
    }
}

В JUnit 4 тестовые классы больше не нужно расширять TestCase и может вместо этого использовать аннотации. Затем вы можете статически импортировать методы assert из org.junit.Assert:

// new way
import static org.junit.Assert.assertEquals;

public class MyTestClass {
    @Test public void myMethodTest() {
        assertEquals("foo", "bar");
        // instead of
        Assert.assertEquals("foo", "bar");
    }
}

JUnit документы, используя его таким образом.

В конце статьи 19 " Эффективная Java", второе издание, отмечается, что вы можете использовать статический импорт, если вы интенсивно используете константы из служебного класса. Я думаю, что этот принцип применим к статическому импорту как констант, так и методов.

import static com.example.UtilityClassWithFrequentlyUsedMethods.myMethod;

public class MyClass {
    public void doSomething() {
        int foo= UtilityClassWithFrequentlyUsedMethods.myMethod();
        // can be written less verbosely as
        int bar = myMethod();
    }
}

Это имеет свои преимущества и недостатки. Это делает код немного более читаемым за счет потери некоторой непосредственной информации о том, где определен метод. Однако хорошая IDE позволит вам перейти к определению, так что это не большая проблема.

Вы все равно должны использовать это экономно, и только если вы обнаружите, что используете вещи из импортированного файла много, много раз.

Изменить: Обновлено, чтобы быть более конкретным для методов, как это то, что этот вопрос относится к. Принцип применяется независимо от того, что импортируется (константы или методы).

Я думаю, что статический импорт действительно полезен для удаления избыточных имен классов при использовании таких утилит, как Arrays а также Assertions,

Не знаю почему, но Росс пропустил последнее предложение, которое упоминает об этом в документации, на которую он ссылается.

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

В основном скопировано из этого блога: https://medium.com/alphadev-thoughts/static-imports-are-great-but-underused-e805ba9b279f

Так, например:

Утверждения в тестах

Это наиболее очевидный случай, с которым, я думаю, мы все согласны

Assertions.assertThat(1).isEqualTo(2);

// Use static import instead
assertThat(1).isEqualTo(2);

Утилиты и перечисления

Имя класса может быть удалено во многих случаях при использовании классов утилит, облегчающих чтение кода

List<Integer> numbers = Arrays.asList(1, 2, 3);

// asList method name is enough information
List<Integer> numbers = asList(1, 2, 3);

В пакете java.time есть несколько случаев, когда его следует использовать

// Get next Friday from now, quite annoying to read
LocalDate.now().with(TemporalAdjusters.next(DayOfWeek.FRIDAY));

// More concise and easier to read
LocalDate.now().with(next(FRIDAY));

Пример, когда не использовать

// Ok this is an Optional
Optional.of("hello world");

// I have no idea what this is 
of("hello world");

Я согласен, что они могут быть проблематичными с точки зрения читабельности и должны использоваться с осторожностью. Но при использовании обычного статического метода они действительно могут улучшить читаемость. Например, в тестовом классе JUnit такие методы, как assertEquals очевидно, откуда они берутся. Аналогично для методов из java.lang.Math,

Я использую его для цвета много.

static import java.awt.Color.*;

Маловероятно, что цвета будут перепутаны с чем-то другим.

Я рекомендую использовать статический импорт при использовании OpenGL с Java, который является случаем использования, попадающим в категорию "интенсивное использование констант из служебного класса"

Считают, что

import static android.opengl.GLES20.*;

позволяет переносить оригинальный код на C и писать что-то читаемое, например:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(samplerUniform, 0);
glBindBuffer(GL_ARRAY_BUFFER, vtxBuffer);
glVertexAttribPointer(vtxAttrib, 3, GL_FLOAT, false, 0, 0);

вместо этого распространенного уродства

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
GLES20.glUniform1i(samplerUniform, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vtxBuffer);
GLES20.glVertexAttribPointer(vtxAttrib, 3, GLES20.GL_FLOAT, false, 0, 0);

Я использую "import static java.lang.Math.*" При переносе тяжелого математического кода из C/C++ в java. Математические методы отображают от 1 до 1, что упрощает анализ переносимого кода без уточнения имени класса.

Я обнаружил, что это очень удобно при использовании классов Utility.

Например, вместо использования: if(CollectionUtils.isNotEmpty(col))

Я могу вместо этого:

import static org.apache.commons.collections.CollectionUtils.isNotEmpty;
if(isNotEmpty(col))

Какой IMO повышает читабельность кода, когда я использую эту утилиту несколько раз в моем коде.

Говоря о модульных тестах: большинство людей используют статический импорт для различных статических методов, которые предоставляют фреймворки, такие как when() или же verify(),

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

И, конечно же, при использовании одного-единственного assert вы должны использовать `assertThat(), это удобно для статического импорта требуемых соответствий hamcrest, как в:

import static org.hamcrest.Matchers.*;

Статический импорт - единственная "новая" функция Java, которую я никогда не использовал и не собираюсь использовать из-за проблем, которые вы только что упомянули.

Я думаю, что статический импорт удобен для NLS в стиле gettext.

import static mypackage.TranslatorUtil._;

//...
System.out.println(_("Hello world."));

Это помечает строку как строку, которая должна быть извлечена, и обеспечивает простой и понятный способ заменить строку ее переводом.

Они полезны для уменьшения словоблудия, особенно в тех случаях, когда вызывается много импортированных методов, и различие между локальными и импортированными методами очевидно.

Один пример: код, который включает в себя несколько ссылок на java.lang.Math

Другой: класс XML-компоновщика, в котором добавление имени класса к каждой ссылке скрывает создаваемую структуру

IMO статический импорт - довольно приятная особенность. Абсолютно верно, что сильная зависимость от статического импорта делает код нечитаемым и трудно понять, к какому классу относится статический метод или атрибут. Тем не менее, по моему опыту, это становится полезной особенностью, особенно при разработке Util классы, которые предоставляют некоторые статические методы и атрибуты. Неоднозначность, возникающую при предоставлении статического импорта, можно обойти, установив стандарты кода. По моему опыту в компании, этот подход является приемлемым и делает код чище и легким для понимания. Предпочтительно я вставляю _ характер перед статическими методами и статическими атрибутами (как-то перенято из C). Очевидно, что этот подход нарушает стандарты именования Java, но обеспечивает ясность кода. Например, если у нас есть класс AngleUtils:

public class AngleUtils {

    public static final float _ZERO = 0.0f;
    public static final float _PI   = 3.14f;

    public static float _angleDiff(float angle1, float angle2){

    }

    public static float _addAngle(float target, float dest){

    }
}

В этом случае статический импорт обеспечивает ясность, а структура кода выглядит для меня более элегантно:

import static AngleUtils.*;

public class TestClass{

    public void testAngles(){

        float initialAngle = _ZERO;
        float angle1, angle2;
        _addAngle(angle1, angle2);
    }
}

Сразу же кто-то может сказать, какой метод или атрибут взят из статического импорта, и он скрывает информацию о классе, которому он принадлежит. Я не предлагаю использовать статический импорт для классов, которые являются неотъемлемой частью модуля и предоставляют статические и нестатические методы, так как в этом случае важно знать, какой класс обеспечивает определенную статическую функциональность.

Вы должны использовать их, когда:

  • Вы хотите использовать switch оператор со значениями перечисления
  • Вы хотите сделать свой код сложным для понимания

Я использую их, когда могу. У меня есть настройки IntelliJ, чтобы напомнить мне, если я забуду. Я думаю, что это выглядит намного чище, чем полное название упаковки.

Другие вопросы по тегам