Qual é um bom caso de uso para importação estática de métodos?


137

Acabei de receber um comentário de revisão de que minha importação estática do método não era uma boa ideia. A importação estática era de um método de uma classe DA, que possui principalmente métodos estáticos. Então, no meio da lógica de negócios, tive uma atividade que aparentemente parecia pertencer à classe atual:

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

O revisor não estava interessado em mudar o código e não o fiz, mas concordo com ele. Uma razão dada para não importar estática foi que era confuso onde o método foi definido, não estava na classe atual e nem em nenhuma superclasse; portanto, demorou muito tempo para identificar sua definição (o sistema de revisão baseado na Web não tem cliques links como IDE :-) Eu realmente não acho que isso importe, as importações estáticas ainda são novas e em breve todos nós vamos nos acostumar a localizá-las.

Mas a outra razão, com a qual concordo, é que uma chamada de método não qualificada parece pertencer ao objeto atual e não deve pular contextos. Mas se realmente pertencesse, faria sentido estender essa super classe.

Então, quando faz sentido métodos de importação estáticos? Quando você fez isso? Você gostou da aparência das chamadas não qualificadas?

EDIT: A opinião popular parece ser que os métodos de importação estática se ninguém os confundirá como métodos da classe atual. Por exemplo, métodos de java.lang.Math e java.awt.Color. Mas se abs e getAlpha não são ambíguos, não vejo por que readEmployee é. Como em muitas opções de programação, acho que isso também é uma preferência pessoal.

Obrigado pela sua resposta pessoal, estou encerrando a pergunta.


2
Aqui está um uso muito bom de importações estáticas: ibm.com/developerworks/library/j-ft18
intrepidis

1
@ MR5 a sintaxe é import static, o recurso éstatic import
Variable Miserable

Respostas:


150

Isso é do guia da Sun quando eles lançaram o recurso (ênfase no original):

Então, quando você deve usar a importação estática? Muito com moderação! Use-o somente quando tiver a tentação de declarar cópias locais de constantes ou abusar da herança (o Antipadrão da interface constante). ... Se você usar em excesso o recurso de importação estática, ele poderá tornar seu programa ilegível e impossível de manter, poluindo seu espaço de nome com todos os membros estáticos importados. Os leitores do seu código (incluindo você, alguns meses depois de escrevê-lo) não saberão de qual classe um membro estático vem. Importar todos os membros estáticos de uma classe pode ser particularmente prejudicial à legibilidade; se você precisar de apenas um ou dois membros, importe-os individualmente.

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

Quero mencionar duas partes especificamente:

  • Use importações estáticas somente quando você foi tentado a "abusar da herança". Nesse caso, você ficaria tentado a ter o BusinessObject extend some.package.DA? Nesse caso, as importações estáticas podem ser uma maneira mais limpa de lidar com isso. Se você nunca sonhou em estender some.package.DA, provavelmente esse é um mau uso das importações estáticas. Não use apenas para salvar alguns caracteres ao digitar.
  • Importar membros individuais. Diga em import static some.package.DA.savevez de DA.*. Isso tornará muito mais fácil descobrir de onde vem esse método importado.

Pessoalmente, usei esse recurso de linguagem muito raramente, e quase sempre apenas com constantes ou enumerações, nunca com métodos. A troca, para mim, quase nunca vale a pena.


9
Acordado. Ocasionalmente, usei importações estáticas onde eles realmente tornaram o código significativamente mais fácil de seguir.
911 Neil Coffey

65

Outro uso razoável para importações estáticas é o JUnit 4. Nas versões anteriores do JUnit, métodos como assertEqualse failforam herdados desde que a classe de teste foi estendida junit.framework.TestCase.

// old way
import junit.framework.TestCase;

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

No JUnit 4, as classes de teste não precisam mais ser estendidas TestCasee podem usar anotações. Em seguida, você pode importar estaticamente os métodos de declaração de 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 documentos usando-o desta maneira.


4
Eu concordo. A simplificação de casos de teste é um local em que é improvável que a intenção seja mal interpretada.
9119 Bill Michell

6
Tivemos isso em nosso projeto e, na verdade, tivemos problemas com as pessoas que usam assert () e pensam incorretamente que é proveniente da importação estática do pacote Assert. Depois que encontramos esse problema, uma verificação rápida de nossa base de código encontrou cerca de 30 instâncias disso em nossos testes, o que significa que 30 asserções NÃO estavam sendo executadas quando a estrutura de teste foi executada porque o sinalizador DEBUG não está definido quando executamos testes.
Chris Williams

27

Effective Java, Second Edition , no final do item 19 notas que você pode usar importações estáticas se você se encontra fortemente usando constantes de uma classe de utilitário. Penso que este princípio se aplicaria às importações estáticas de constantes e métodos.

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

Isto tem vantagens e desvantagens. Isso torna o código um pouco mais legível à custa de perder algumas informações imediatas sobre onde o método está definido. No entanto, um bom IDE permitirá que você vá para a definição, portanto, isso não é um problema.

Você ainda deve usá-lo com moderação e somente se se deparar com coisas do arquivo importado muitas e muitas vezes.

Editar: atualizado para ser mais específico aos métodos, pois é a isso que esta pergunta se refere. O princípio se aplica independentemente do que está sendo importado (constantes ou métodos).


1
Minha pergunta é sobre métodos de importação estática , não campos.
Variável miserável

7
Talvez UtilityClassWithFrequentlyUsedMethodsprecise ser reduzido.
Steve Kuo


@ Rob-Hruska Não seria possível agrupar um método ou campo estático de importação em um novo método ou campo se estiver planejando usá-los com frequência? Isso me permitiria não importar estaticamente? como: double myPI = Math.PI;e então eu posso continuar me referindo ao myPIinvés de Math.PI.
Abdul

@Abdul - Sim, você poderia fazer isso.
Rob Hruska

14

Concordo que eles podem ser problemáticos do ponto de vista da legibilidade e devem ser usados ​​com moderação. Porém, ao usar um método estático comum, eles podem realmente aumentar a legibilidade. Por exemplo, em uma classe de teste JUnit, métodos como assertEqualssão óbvios de onde eles vêm. Da mesma forma para métodos de java.lang.Math.


5
E o que há de tão ruim em ver Math.round (d) versus round (d)?
Steve Kuo

5
@SteveKuo - pela mesma razão que os matemáticos usam nomes de variáveis ​​de uma letra ao manipular fórmulas: há momentos em que nomes mais longos interferem na legibilidade da declaração geral. Considere uma fórmula envolvendo várias funções trigonométricas. Uma fórmula matemática facilmente compreendido: sin x cos y + cos x sin y. Em Java torna-se: Math.sin(x) * Math.cos(y) + Math.cos(x) * Math.sin(y). Horrível de ler.
Página

@ ToolmakerSteve, é por isso que perdi tanto a usingdiretiva em C ++: elas podem ser locais .
Franklin Yu

11

Eu acho que a importação estática é realmente útil para remover nomes de classes redundantes ao usar classes utils como Arrayse Assertions.

Não sei por que, mas Ross pulou a última frase que menciona isso na documentação que ele está referenciando .

Usada adequadamente, a importação estática pode tornar seu programa mais legível, removendo o padrão da repetição de nomes de classe.

Basicamente, copiado deste blog: https://medium.com/alphadev-thoughts/static-imports-are-great-but-underused-e805ba9b279f

Então, por exemplo:

Asserções em testes

Este é o caso mais óbvio com o qual acho que todos concordamos.

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

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

Classes de utils e enums

O nome da classe pode ser removido em muitos casos ao usar classes utils, facilitando a leitura do código

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

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

O pacote java.time possui alguns casos em que deve ser usado

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

Exemplo de quando não usar

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

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

10

Eu o uso muito para cores.

static import java.awt.Color.*;

É muito improvável que as cores sejam confundidas com outra coisa.


1
Esse é um dos melhores casos de uso que vi que difere do antigo JUnit / Hamcrest / TestNG.
Kevinarpe

3

Eu recomendo o uso de importação estática ao usar OpenGL com Java, que é um caso de uso cair no "uso pesado de constantes de uma classe de utilitário" categoria

Considere isso

import static android.opengl.GLES20.*;

permite portar o código C original e escrever algo legível, como:

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

em vez dessa feiúra generalizada:

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

As importações estáticas são o único recurso "novo" do Java que eu nunca usei e não pretendo usar, devido aos problemas que você acabou de mencionar.


Obrigado Bombe. Bem, acredito que eles fazem mais sentido do que ter que estender e fazer a interface que contém apenas um monte de finais estáticos.
Variável miserável

2

Eu uso 'import static java.lang.Math. *' Ao portar código pesado de matemática de C / C ++ para java. Os métodos matemáticos mapeiam de 1 a 1 e facilitam a diferenciação do código portado sem a qualificação do nome da classe.


2

Achei isso muito conveniente ao usar as classes Utility.

Por exemplo, em vez de usar: if(CollectionUtils.isNotEmpty(col))

Eu posso:

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

Qual IMO aumenta a legibilidade do código quando eu uso esse utilitário várias vezes no meu código.


2

Falando sobre testes de unidade: a maioria das pessoas usa importações estáticas para os vários métodos estáticos fornecidos pelas estruturas de simulação , como when()ou verify().

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

E, é claro, ao usar a única e única afirmação de que você deve usá- assertThat()la, é útil importar estaticamente os matchers de hamcrest necessários, como em:

import static org.hamcrest.Matchers.*;

1

Eles são úteis para reduzir a verborragia, principalmente nos casos em que existem muitos métodos importados sendo chamados, e a distinção entre métodos locais e importados é clara.

Um exemplo: código que envolve várias referências a java.lang.Math

Outra: Uma classe de construtor XML em que a adição do nome da classe a todas as referências ocultaria a estrutura que está sendo construída


1

Eu acho que as importações estáticas são legais para o NLS no estilo gettext.

import static mypackage.TranslatorUtil._;

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

Isso marca a sequência como uma sequência que precisa ser extraída e fornece uma maneira fácil e limpa de substituir a sequência pela tradução.


1

A importação estática da IMO é um recurso bastante interessante. É absolutamente verdade que a forte dependência da importação estática torna o código ilegível e difícil de entender a qual classe um método ou atributo estático pertence. No entanto, na minha experiência, ele se torna um recurso utilizável, especialmente ao projetar Utilclasses que fornecem alguns métodos e atributos estáticos. A ambiguidade que surge sempre que a importação estática é fornecida pode ser contornada através do estabelecimento de padrões de código. Na minha experiência em uma empresa, essa abordagem é aceitável e torna o código mais limpo e fácil de entender. De preferência, insiro o _caractere nos métodos estáticos da frente e nos atributos estáticos (de alguma forma adotados em C). Aparentemente, essa abordagem viola os padrões de nomenclatura do Java, mas fornece clareza ao código. Por exemplo, se tivermos uma classe 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){

    }
}

Nesse caso, a importação estática fornece clareza e a estrutura do código parece mais elegante para mim:

import static AngleUtils.*;

public class TestClass{

    public void testAngles(){

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

Imediatamente alguém pode dizer qual método ou atributo vem de uma importação estática e oculta as informações da classe à qual pertence. Não sugiro o uso de importação estática para classes que fazem parte integrante de um módulo e forneço métodos estáticos e não estáticos, pois nesse caso é importante saber qual classe fornece certa funcionalidade estática.


Obrigado pela sugestão de mudar o nome. BTW, um sublinhado na frente é tradicionalmente usado em alguns ambientes para nomear métodos / campos privados. Estou considerando uma convenção modificada, como H_para importações de uma Helperclasse de utilitário que possuo, ou C_para Commonou U_para Utility. Como alternativa, considerei o uso de um ou dois nomes de classes de caracteres para essas classes amplamente usadas, mas fiquei preocupado com o fato de algumas vezes poderem entrar em conflito com nomes locais - ter algum código legado com nomes de métodos em maiúsculas.
precisa

-1

Você precisa usá-los quando:

  • você deseja usar uma switchdeclaração com valores enum
  • você deseja tornar seu código difícil de entender

9
Isso não é verdade. (1) Você pode usar constantes enum perfeitamente bem sem uma importação estática delas. (2) As importações estáticas dos métodos da classe JUnit Assert, por exemplo, são claras como um sino. "assertTrue (...)" é tão legível quanto "Assert.assertTrue (...)", talvez mais.
Alan Krueger

5
se você tiver 5 importações estáticas em uma classe de 500 linhas, é muito difícil saber de onde vêm os métodos.
Davetron5000 13/01/2009

4
+1 para quando você deseja dificultar a compreensão do seu código :)
Variável miserável

-5

Eu os uso sempre que posso. Eu tenho a instalação do IntelliJ para me lembrar se eu esquecer. Eu acho que parece muito mais limpo do que um nome de pacote totalmente qualificado.


13
Você está pensando em importações regulares. As importações estáticas permitem que você se refira aos membros de uma classe sem qualificá-los com um nome de classe, por exemplo, importação estática java.lang.system.out; out.println ("foo"); // em vez de System.out.println ("foo");
sk.

Agora esta é uma explicação muito boa de importações estáticas ... muito ruim Eu não +1 pode um comentário
Eldelshell
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.