Який хороший випадок використання для статичного імпорту методів?


137

Щойно отримав коментар з огляду, що мій статичний імпорт методу був недоброю ідеєю. Статичний імпорт був методом із класу DA, який має в основному статичні методи. Тож в середині ділової логіки у мене відбулася діяльність да, яка, здавалося б, належить до поточного класу:

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

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

Але інша причина, з якою я погоджуюсь, полягає в тому, що некваліфікований виклик методу, здається, належить поточному об'єкту і не повинен стрибати контексти. Але якби це дійсно належало, було б сенс розширити цей суперклас.

Отже, коли має сенс статичні методи імпорту? Коли ви це зробили? Вам подобається, як виглядають некваліфіковані дзвінки?

EDIT: Думається, що статичні методи імпорту, якщо ніхто не збирається плутати їх як методи поточного класу. Наприклад, методи з java.lang.Math та java.awt.Color. Але якщо abs та getAlpha не неоднозначні, я не розумію, чому саме читаєEEEEEEEEEEEE. Як і у багатьох варіантах програмування, я вважаю, що це теж є особистою перевагою.

Дякую за вашу відповідь, хлопці, я закриваю питання.


2
Ось дуже вдале використання статичного імпорту: ibm.com/developerworks/library/j-ft18
intrepidis

1
@ mr5 синтаксис є import static, особливістю єstatic import
Жалюгідна змінна

Відповіді:


150

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

Тож коли слід використовувати статичний імпорт? Дуже щадно! Використовуйте його лише тоді, коли в іншому випадку ви б спокусилися оголосити локальні копії констант або зловживати спадщиною (постійний інтерфейс Antipattern). ... Якщо ви зловживаєте функцією статичного імпорту, вона може зробити вашу програму нечитабельною та нездійсненою, забруднивши її простір імен усіма статичними членами, які ви імпортуєте. Читачі вашого коду (включаючи вас, кілька місяців після того, як ви його написали) не дізнаються, з якого класу походить статичний член. Імпорт усіх статичних членів з класу може бути особливо шкідливим для читабельності; якщо вам потрібен лише один або два учасники, імпортуйте їх окремо.

( 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.*. Це дозволить набагато простіше знайти, звідки береться цей імпортний метод.

Особисто я користувався цією мовною особливістю дуже рідко, і майже завжди лише з константами чи перерахунками, ніколи з методами. Для мене компроміс майже ніколи не вартий.


9
Домовились. Іноді я використовую статичний імпорт вієрі, де вони фактично зробили код значно простішим.
Ніл Коффі

65

Ще одне розумне використання для статичного імпорту - з 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а натомість вони можуть використовувати анотації. Потім ви можете статично імпортувати методи затвердження з 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 документи, використовуючи це таким чином.


4
Я погодився б. Спрощення тестових випадків - це одне місце, де наміри навряд чи будуть зрозумілі неправильно.
Білл Мішель

6
У нас це було в нашому проекті, і насправді виникли проблеми з людьми, які використовують assert () і помилково думають, що це відбувається від їх статичного імпорту пакету Assert. Коли ми знайшли цю проблему, швидке сканування нашої кодової бази виявило близько 30 екземплярів цього в наших тестах, тобто 30 тверджень НЕ виконувались під час виконання тестової основи, оскільки прапор DEBUG не встановлений під час запуску тестів.
Кріс Вільямс

27

Ефективна Java, друге видання , наприкінці пункту 19 зазначає, що ви можете використовувати статичний імпорт, якщо ви сильно використовуєте константи класу корисності. Я думаю, що цей принцип застосовуватиметься до статичного імпорту констант та методів.

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 дозволить вам перейти до визначення, тому це не є великою проблемою.

Ви все-таки повинні користуватися цим ощадно, і лише якщо ви знайдете, що ви використовуєте речі з імпортованого файлу багато-багато разів.

Редагувати: оновлено, щоб бути більш конкретними для методів, оскільки саме до цього питання йдеться. Принцип застосовується незалежно від того, що імпортується (константи чи методи).


1
Моє запитання стосується статичних імпортних методів , а не полів.
Жалюгідна змінна

7
Можливо, це UtilityClassWithFrequentlyUsedMethodsпотрібно скоротити.
Стів Куо

5
@SteveKuo звичайно менше, ніж InternalFrameTitlePaneMaximizeButtonWindowNotFocusedState : P
Anirban Nag 'tintinmj'

@ Rob-Hruska Не можу я просто перетворити статичний метод імпорту або поле в новий метод або поле, якщо я планую використовувати їх часто? Чи дозволить мені статично не імпортувати? наприклад: double myPI = Math.PI;і тоді я можу просто продовжувати посилатися на, myPIа не на Math.PI.
Абдул

@Abdul - Так, ти можеш це зробити.
Роб Грушка

14

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


5
І що такого поганого в тому, щоб бачити Math.round (d) проти туру (d)?
Стів Куо

5
@SteveKuo - з тієї ж причини, що математики використовують однобуквені імена змінних при маніпулюванні формулами: трапляються випадки, коли довші імена заважають читати загальний вислів. Розглянемо формулу, що включає декілька тригонометричних функцій. Легко зрозуміти математику формула: sin x cos y + cos x sin y. В Java стає: Math.sin(x) * Math.cos(y) + Math.cos(x) * Math.sin(y). Жахливо читати.
ToolmakerSteve

@ToolmakerSteve, тому я usingтак сильно пропустив директиву в C ++: вони можуть бути локальними .
Франклін Ю

11

Я думаю, що статичний імпорт дійсно корисний для видалення зайвих імен класів при використанні класів утилітів, таких як Arraysі Assertions.

Не знаю чому, але Росс пропустив останнє речення, яке згадує про це в документації, на яку він посилається .

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

В основному скопійовано з цього блогу: https://medium.com/alphadev-

Так, наприклад:

Твердження в тестах

Це найочевидніший випадок, з яким я думаю, що ми всі згодні

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");

10

Я використовую його для Color дуже багато.

static import java.awt.Color.*;

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


1
Це один із найкращих випадків використання, який я бачив, який відрізняється від старого JUnit / Hamcrest / TestNG.
kevinarpe

3

Я рекомендую використовувати статичний імпорт при використанні 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);

2

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


Спасибі Бомбе. Що ж, я вважаю, що вони мають кращий сенс у тому, що потрібно розширювати та інтерфейс, що містить просто статичний фінал.
Жалюгідна змінна

2

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


2

Я вважав це дуже зручним під час використання класів Utility.

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

Я можу замість цього:

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

Який IMO збільшує читабельність коду, коли я використовую цю утиліту кілька разів у своєму коді.


2

Якщо говорити про одиничні тести: більшість людей використовують статичний імпорт для різних статичних методів, які надають глузуючі рамки, такі як when()або verify().

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

І звичайно, коли ви користуєтесь одним і єдиним твердженням, вам слід скористатися, assertThat()це стане в нагоді статичним імпортом потрібних підборщиків хомута, як у:

import static org.hamcrest.Matchers.*;

1

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

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

Інший: клас XML- конструктора, де попередньо додавши ім'я класу до кожної посилання, було б приховано структуру, що будується


1

Я думаю, що статичний імпорт є чітким для NLS у стилі gettext.

import static mypackage.TranslatorUtil._;

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

Це обидва позначає рядок як рядок, яку потрібно витягти, і забезпечує простий і чистий спосіб замінити рядок його перекладом.


1

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

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);
    }
}

Одразу хтось може сказати, який метод або атрибут походить від статичного імпорту, і він приховує інформацію про клас, якому він належить. Я не пропоную використовувати статичний імпорт для класів, які є невід'ємною частиною модуля і надають статичні та нестатичні методи, оскільки в цьому випадку важливо знати, який клас забезпечує певну статичну функціональність.


Дякуємо за пропозицію щодо перейменування. BTW, підкреслення в передній частині традиційно використовується в деяких середовищах для назви приватних методів / полів. Я розглядаю модифіковану конвенцію, наприклад, H_щодо імпорту з Helperкорисного класу, який я маю, або C_для Common, або U_для Utility. Крім того, я розглядав можливість використання одного або двох імен класів символів для цих широко використовуваних класів, але занепокоєний тим, що іноді вони можуть суперечити локальним іменам - мати деякий застарілий код із великими іменами методу.
ToolmakerSteve

-1

Використовувати їх потрібно, коли:

  • Ви хочете використовувати switchоператор із значеннями enum
  • Ви хочете зробити свій код важким для розуміння

9
Це не правда. (1) Ви можете безперервно використовувати константи enum без статичного імпортування їх. (2) Статичний імпорт, скажімо, методів класу JUnit Assert зрозумілий як дзвін. "assertTrue (...)" так само читабельний, як "Assert.assertTrue (...)", можливо, більше.
Алан Крюгер

5
якщо у вас є 5 статичних імпортів у 500 лінійному класі, дуже важко сказати, звідки беруться методи.
davetron5000

4
+1, коли ви хочете зробити свій код важким для розуміння :)
Жалюгідна змінна

-5

Я використовую їх коли-небудь можу. У мене є програма IntelliJ, щоб нагадати, якщо я забуду. Я думаю, що це виглядає набагато чіткіше, ніж повністю кваліфікована назва пакета.


13
Ви думаєте про регулярний імпорт. Статичний імпорт дозволяє вам посилатися на членів класу, не кваліфікуючи їх з іменем класу, наприклад, статичний імпорт java.lang.system.out; out.println ("foo"); // замість System.out.println ("foo");
ск.

Тепер це дуже хороше пояснення статичного імпорту ... шкода, що я не можу поставити коментар +1
Eldelshell
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.