Eu gostaria de poder escrever uma classe Java em um pacote que possa acessar métodos não públicos de uma classe em outro pacote sem precisar torná-la uma subclasse da outra classe. Isso é possível?
Aqui está um pequeno truque que eu uso no JAVA para replicar o mecanismo de amigo do C ++.
Digamos que eu tenho uma aula Romeo
e outra aula Juliet
. Eles estão em pacotes diferentes (família) por razões de ódio.
Romeo
quer cuddle
Juliet
e Juliet
quer apenas deixá- Romeo
cuddle
la.
Em C ++, Juliet
declararia Romeo
como um (amante), friend
mas não existem tais coisas em java.
Aqui estão as aulas e o truque:
Damas primeiro :
package capulet;
import montague.Romeo;
public class Juliet {
public static void cuddle(Romeo.Love love) {
Objects.requireNonNull(love);
System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
}
}
Portanto, o método Juliet.cuddle
é, public
mas você precisa Romeo.Love
chamá-lo. Ele usa isso Romeo.Love
como uma "segurança de assinatura" para garantir que apenas seja Romeo
possível chamar esse método e verifica se o amor é real para que o tempo de execução gere um NullPointerException
se for null
.
Agora meninos:
package montague;
import capulet.Juliet;
public class Romeo {
public static final class Love { private Love() {} }
private static final Love love = new Love();
public static void cuddleJuliet() {
Juliet.cuddle(love);
}
}
A classe Romeo.Love
é pública, mas seu construtor é private
. Portanto, qualquer um pode vê-lo, mas apenas Romeo
pode construí-lo. Eu uso uma referência estática para Romeo.Love
que o que nunca é usado seja construído apenas uma vez e não afete a otimização.
Portanto, Romeo
pode cuddle
Juliet
e só ele pode, porque só ele pode construir e acesso de um Romeo.Love
exemplo, que é exigido por Juliet
que cuddle
ela (ou então ela vai golpeá-lo com um NullPointerException
).
Romeo
'sLove
paraJulia
a eternidade, alterando olove
campo para serfinal
;-).Os designers de Java rejeitaram explicitamente a idéia de amigo, pois funciona em C ++. Você coloca seus "amigos" no mesmo pacote. A segurança privada, protegida e empacotada é imposta como parte do design do idioma.
James Gosling queria que o Java fosse C ++ sem os erros. Acredito que ele sentiu que esse amigo foi um erro porque viola os princípios de POO. Os pacotes fornecem uma maneira razoável de organizar componentes sem ser muito purista em relação ao POO.
NR apontou que você poderia trapacear usando a reflexão, mas mesmo isso só funciona se você não estiver usando o SecurityManager. Se você ativar a segurança padrão Java, não poderá enganar-se com reflexão, a menos que escreva uma política de segurança para permitir especificamente.
fonte
friend
violava a OOP (em particular, mais do que o acesso a pacotes), ele realmente não a entendeu (inteiramente possível, muitas pessoas entendem mal).O conceito de 'amigo' é útil em Java, por exemplo, para separar uma API de sua implementação. É comum que as classes de implementação precisem acessar as classes internas da API, mas elas não devem ser expostas aos clientes da API. Isso pode ser alcançado usando o padrão 'Friend Accessor', conforme detalhado abaixo:
A classe exposta através da API:
A classe que fornece a funcionalidade 'friend':
Exemplo de acesso de uma classe no pacote de implementação 'friend':
fonte
Existem duas soluções para sua pergunta que não envolvem manter todas as classes no mesmo pacote.
A primeira é usar o padrão Friend Accessor / Friend Package descrito em (Practical API Design, Tulach 2008).
O segundo é usar o OSGi. Há um artigo aqui explicando como o OSGi realiza isso.
Perguntas relacionadas: 1 , 2 e 3 .
fonte
Tanto quanto eu sei, não é possível.
Talvez você possa nos dar mais alguns detalhes sobre o seu design. Perguntas como essas provavelmente são o resultado de falhas de design.
Apenas considere
fonte
A resposta da eirikma é fácil e excelente. Posso acrescentar mais uma coisa: em vez de ter um método acessível ao público, getFriend () para obter um amigo que não pode ser usado, você pode dar um passo adiante e impedir que ele seja aceito sem um token: getFriend (Service.FriendToken). Esse FriendToken seria uma classe pública interna com um construtor privado, para que apenas o Serviço pudesse instanciar um.
fonte
Aqui está um exemplo claro de caso de uso com uma
Friend
classe reutilizável . O benefício desse mecanismo é a simplicidade de uso. Talvez seja bom para dar mais acesso às classes de teste de unidade que o restante do aplicativo.Para começar, aqui está um exemplo de como usar a
Friend
classe.Em outro pacote, você pode fazer isso:
A
Friend
classe é a seguinte.No entanto, o problema é que ele pode ser abusado da seguinte maneira:
Agora, pode ser verdade que a
Other
classe não tenha nenhum construtor público, tornandoAbuser
impossível o código acima . No entanto, se sua classe não tem um construtor público, então provavelmente é aconselhável duplicar a classe amigo de uma classe interna. Tome estaOther2
classe como um exemplo:E então a
Owner2
classe seria assim:Observe que a
Other2.Friend
classe tem um construtor privado, tornando assim uma maneira muito mais segura de fazê-lo.fonte
A solução fornecida talvez não fosse a mais simples. Outra abordagem é baseada na mesma idéia do C ++: membros privados não são acessíveis fora do pacote / escopo privado, exceto para uma classe específica em que o proprietário faz um amigo.
A classe que precisa de acesso de amigo a um membro deve criar uma "classe de amigo" abstrata pública interna à qual a classe que possui as propriedades ocultas pode exportar o acesso, retornando uma subclasse que implementa os métodos de implementação de acesso. O método "API" da classe de amigo pode ser privado, portanto, não é acessível fora da classe que precisa de acesso de amigo. Sua única declaração é uma chamada para um membro protegido abstrato que a classe exportadora implementa.
Aqui está o código:
Primeiro, o teste que verifica se isso realmente funciona:
Em seguida, o Serviço que precisa de acesso de amigo a um membro privado do pacote da Entidade:
Finalmente: a classe Entity que fornece acesso amigável a um membro privado do pacote apenas para a classe application.service.Service.
Ok, devo admitir que é um pouco mais longo que "friend service :: Service;" mas pode ser possível reduzi-lo, mantendo a verificação em tempo de compilação usando anotações.
fonte
Em Java, é possível ter uma "amizade relacionada ao pacote". Isso pode ser útil para testes de unidade. Se você não especificar privado / público / protegido na frente de um método, ele será "amigo no pacote". Uma classe no mesmo pacote poderá acessá-la, mas será privada fora da classe.
Essa regra nem sempre é conhecida e é uma boa aproximação de uma palavra-chave "amigo" em C ++. Acho um bom substituto.
fonte
Eu acho que as classes de amigo em C ++ são como o conceito de classe interna em Java. Usando classes internas, você pode realmente definir uma classe anexa e uma classe fechada. A classe fechada tem acesso total aos membros públicos e privados da classe envolvente. consulte o seguinte link: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
fonte
Eu acho que a abordagem de usar o padrão de acessador de amigos é muito complicada. Eu tive que enfrentar o mesmo problema e resolvi usando o bom e antigo construtor de cópias, conhecido em C ++, em Java:
No seu aplicativo, você pode escrever o seguinte código:
A vantagem desse método é que apenas seu aplicativo tem acesso aos dados protegidos. Não é exatamente uma substituição da palavra-chave friend. Mas acho que é bastante adequado quando você escreve bibliotecas personalizadas e precisa acessar dados protegidos.
Sempre que você precisar lidar com instâncias do ProtectedContainer, envolva seu ProtectedAccessor e obtenha acesso.
Também funciona com métodos protegidos. Você os define protegidos em sua API. Posteriormente em seu aplicativo, você escreve uma classe de invólucro particular e expõe o método protegido como público. É isso aí.
fonte
ProtectedContainer
pode ser subclassificado fora do pacote!Se você deseja acessar métodos protegidos, pode criar uma subclasse da classe que deseja usar que expõe os métodos que você deseja usar como público (ou interno para o namespace para ser mais seguro) e ter uma instância dessa classe em sua classe. (use-o como um proxy).
No que diz respeito aos métodos privados (eu acho), você está sem sorte.
fonte
Concordo que, na maioria dos casos, a palavra-chave friend é desnecessária.
E, finalmente, se for realmente necessário, existe o padrão de acessador de amigo mencionado nas outras respostas.
fonte
Não está usando uma palavra-chave ou algo assim.
Você poderia "trapacear" usando reflexão etc., mas eu não recomendaria "trapacear".
fonte
Um método que encontrei para resolver esse problema é criar um objeto acessador, assim:
O primeiro código a chamar
getAccessor()
"reivindica propriedade" do acessador. Geralmente, esse é o código que cria o objeto.Isso também tem uma vantagem sobre o mecanismo de amigo do C ++, porque permite limitar o acesso em um nível por instância , em oposição a um nível por classe . Controlando a referência do acessador, você controla o acesso ao objeto. Você também pode criar vários acessadores e conceder acesso diferente a cada um, o que permite um controle refinado sobre qual código pode acessar o que:
Por fim, se quiser que as coisas sejam um pouco mais organizadas, você pode criar um objeto de referência, que mantém tudo junto. Isso permite reivindicar todos os acessadores com uma chamada de método, além de mantê-los juntos com sua instância vinculada. Depois de ter a referência, você pode passar os acessadores para o código que precisa:
Depois de muita pancada na cabeça (não do tipo bom), essa foi minha solução final, e eu gostei muito. É flexível, simples de usar e permite um controle muito bom sobre o acesso à classe. (O acesso somente com referência é muito útil.) Se você usar protegido em vez de privado para os acessadores / referências, as subclasses de Foo podem até retornar referências estendidas de
getReference
. Também não requer reflexão, portanto pode ser usado em qualquer ambiente.fonte
A partir do Java 9, os módulos podem ser usados para tornar isso um problema não em muitos casos.
fonte
Prefiro delegação ou composição ou classe de fábrica (dependendo do problema que resulta nesse problema) para evitar torná-lo uma classe pública.
Se for um problema "interface / classes de implementação em pacotes diferentes", eu usaria uma classe de fábrica pública que estaria no mesmo pacote que o pacote impl e impediria a exposição da classe impl.
Se for um problema "Eu odeio tornar esta classe / método público apenas para fornecer essa funcionalidade a alguma outra classe em um pacote diferente", eu usaria uma classe delegada pública no mesmo pacote e exporia apenas essa parte da funcionalidade necessário pela classe "de fora".
Algumas dessas decisões são orientadas pela arquitetura de carregamento de classe do servidor de destino (pacote configurável OSGi, WAR / EAR etc.), convenções de implementação e nomeação de pacotes. Por exemplo, a solução proposta acima, o padrão 'Friend Accessor' é inteligente para aplicativos java normais. Gostaria de saber se fica complicado implementá-lo no OSGi devido à diferença no estilo de carregamento de classe.
fonte
Eu não sei se é de alguma utilidade para alguém, mas eu lidei com isso da seguinte maneira:
Eu criei uma interface (AdminRights).
Toda classe que deve ser capaz de chamar essas funções deve implementar AdminRights.
Então eu criei uma função HasAdminRights da seguinte maneira:
fonte
Certa vez, vi uma solução baseada em reflexão que fazia "verificação de amigos" em tempo de execução usando reflexão e verificação da pilha de chamadas para ver se a classe que chamava o método tinha permissão para fazê-lo. Sendo uma verificação de tempo de execução, tem a desvantagem óbvia.
fonte