Tenho um problema com a ocultação de nomes que é extremamente difícil de resolver. Aqui está uma versão simplificada que explica o problema:
Existe uma classe: org.A
package org;
public class A{
public class X{...}
...
protected int net;
}
Então há uma aula net.foo.X
package net.foo;
public class X{
public static void doSomething();
}
E agora, aqui está a classe problemática que herda A
e deseja chamarnet.foo.X.doSomething()
package com.bar;
class B extends A {
public void doSomething(){
net.foo.X.doSomething(); // doesn't work; package net is hidden by inherited field
X.doSomething(); // doesn't work; type net.foo.X is hidden by inherited X
}
}
Como você vê, isso não é possível. Não posso usar o nome simples X
porque está oculto por um tipo herdado. Não posso usar o nome totalmente qualificado net.foo.X
porque net
está oculto por um campo herdado.
Apenas a classe B
está em minha base de código; as classes net.foo.X
e org.A
são classes da biblioteca, então não posso alterá-las!
Minha única solução é a seguinte: eu poderia chamar outra classe que, por sua vez, chamaria X.doSomething()
; mas essa classe só existiria por causa do nome clash, que parece muito confuso! Será que não há solução em que eu posso chamar diretamente X.doSomething()
a partir de B.doSomething()
?
Em uma linguagem que permite especificar o namespace global, por exemplo, global::
em C # ou ::
em C ++, eu poderia simplesmente prefixar net
com este prefixo global, mas Java não permite isso.
fonte
public void help(net.foo.X x) { x.doSomething(); }
e ligue comhelp(null);
net.foo.X
tem o método, nãoorg.A.X
!A
? Herança pode ser tão desagradável, como você descobriu ...I could call another class that in turn calls X.doSomething(); but this class would only exist because of the name clash, which seems very messy
1 para atitude de código limpo. Mas, para mim, parece que essa é uma situação em que você deve fazer uma troca. Basta fazer isso e lançar um comentário longo e agradável sobre por que você teve que fazer isso (provavelmente com um link para esta pergunta).Respostas:
Você pode converter um
null
para o tipo e, em seguida, invocar o método naquele (o que funcionará, já que o objeto de destino não está envolvido na invocação de métodos estáticos).Isso tem os benefícios de
net.foo.X
),B
o nome que deseja; é por isso que umimport static
não funcionará no seu caso),A desvantagem é que esse código é realmente horrível! Para mim, isso gera um aviso, e isso é bom em geral. Mas, uma vez que está contornando um problema que, de outra forma, seria totalmente impraticável, adicionar um
em um ponto delimitador apropriado (mínimo!) irá desligar o compilador.
fonte
doSomething
método em qualquer lugar em sua hierarquia), então sim, a melhor solução.Provavelmente, a maneira mais simples (não necessariamente a mais fácil) de gerenciar isso seria com uma classe delegada:
e depois ...
Isso é um tanto prolixo, mas muito flexível - você pode fazer com que ele se comporte da maneira que quiser; além disso, ele funciona quase da mesma maneira com
static
métodos e objetos instanciadosMais sobre objetos delegados aqui: http://en.wikipedia.org/wiki/Delegation_pattern
fonte
Você pode usar uma importação estática:
Cuidado com isso
B
eA
não contém métodos nomeadosdoSomething
fonte
doSomething
como o nome do método emB
...doSomething
atinge qualquer lugar na hierarquia de herança (devido a como os destinos de chamada de método são resolvidos). Então o Knuth o ajudará se você quiser chamar umtoString
método. A solução proposta por Donal é a que está no livro Java Puzzlers de Bloch (eu acho, não a procurei), então podemos considerá-la a resposta oficial realmente :-)A maneira correta de fazer as coisas seria a importação estática, mas no pior cenário absoluto, você PODERIA construir uma instância da classe usando reflexão se souber seu nome totalmente qualificado.
Java: newInstance de classe que não possui construtor padrão
E então invoque o método na instância.
Ou apenas invoque o próprio método com reflexão: Invocando um método estático usando reflexão
Claro, esses são obviamente os últimos recursos.
fonte
static import
recurso foi adicionado apenas emJava 1.5
. Não invejo as pessoas que precisam desenvolver para 1.4 ou inferior, tive que fazer uma vez e foi terrível!((net.foo.X)null).doSomething()
solução que funciona no Java antigo. Mas se o tipoA
ainda contém um tipo internonet
, ENTÃO esta é a única resposta que permanece válida :).Não é realmente A resposta, mas você pode criar uma instância de X e chamar o método estático nela. Essa seria uma forma (suja, admito) de chamar o seu método.
fonte
null
para o tipo e, em seguida, chamar o método nele seria melhor, já que criar um objeto pode ter efeitos colaterais que eu não desejo.null
parece ser uma das maneiras mais limpas de fazer isso. Precisa@SuppressWarnings("static-access")
ser livre de avisos ...null
, mas o elenconull
sempre foi uma quebra de coração para mim.Não há necessidade de fazer qualquer elenco ou suprimir quaisquer avisos estranhos ou criar qualquer instância redundante. Apenas um truque usando o fato de que você pode chamar métodos estáticos da classe pai por meio da subclasse. (Semelhante à minha solução hackeada aqui .)
Basta criar uma classe como esta
(O construtor privado nesta classe final garante que ninguém possa criar acidentalmente uma instância desta classe.)
Então você está livre para ligar
X.doSomething()
por meio dele:fonte
E se você tentar obter o gobalnamespace, já que todos os arquivos estão na mesma pasta. ( http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html )
fonte
getGlobal()
não é um método Java padrão para pacotes ... (acho que os pacotes não podem ter nenhum método em Java ...)Essa é uma das razões pelas quais a composição é preferível à herança.
fonte
Eu usaria o padrão de estratégia.
Agora você também desacoplou sua dependência, para que seus testes de unidade sejam mais fáceis de escrever.
fonte
implements SomethingStrategy
naXSomethingStrategy
declaração da classe.