Pergunta

No programa que eu estou escrevendo eu tenho um RestrictedUser classe e User classe que é derivado de RestrictedUser. eu estou tentando esconder os métodos específicos de usuários por vazamento para RestrictedUser mas quando eu faço a lançar os métodos de usuários ainda estão disponíveis. Além disso, quando eu executar o depurador do tipo da variável surge como User.

RestrictedUser restricted = regularUser;

Será que se lançando em esconder Java métodos de subclasse e campos ou estou fazendo algo errado? Existe uma solução?

Graças

Foi útil?

Solução

Se você estava a tentar executar este código:

User user = new User(...);
RestrictedUser restricted = user;
restricted.methodDefinedInTheUserClass(); // <--

você teria um erro de compilação. Isso não vai ser seguro em qualquer maneira, forma ou formulário, porque mesmo se estivesse a passar em torno da RestrictedUser para, digamos, um outro método, esse método poderia fazer isso:

if (restricted instanceof User) {
    User realUser = (User)restricted;
    realUser.methodDefinedInTheUserClass(); // !!
}

e seu "restrito" usuário não é tão mais restrito.

O objeto está aparecendo no depurador como um objeto User porque é como objeto User, mesmo se a referência de objeto é armazenado em uma variável RestrictedUser. Basicamente, colocando uma instância de uma classe filha em uma variável com o tipo de uma classe pai vai realmente "esconder" métodos e campos da subclasse, mas não de forma segura.

Outras dicas

Eu não sei exatamente o que você está perguntando como a terminologia que você está usando é um pouco claro, mas aqui vai. Se você tem uma superclasse e uma subclasse você pode esconder os métodos na superclasse da subclasse, tornando-privada. Se você precisa de métodos públicos na superclasse para ser invisível, você está fora de sorte. Se você lançar a subclasse para a superclasse, em seguida, os métodos públicos nas subclasse não são mais visíveis.

Uma sugestão que pode ajudá-lo seria a composição uso ao invés de herança, extrair os métodos sensíveis em uma classe separada e, em seguida, insira uma instância apropriada dessa classe para o objeto, conforme necessário. Você pode delegar métodos da classe para a classe injetada se você precisa.

Você está confundindo tipo estático e dinâmico tipo.

tipo estático é o tipo de referência. Quando você up-cast de Derivado de Base, você está dizendo ao compilador que tanto quanto você e que sabe, a coisa apontou para uma Base. Ou seja, você está prometendo que o objeto apontado é ou nulo, ou uma base, ou algo derivado de Base. O que quer dizer, tem interface pública de Base.

Você não vai, então, ser capaz de chamar métodos declarados em Derivado (e não na base), mas quando você chamar os métodos da Base substituídas por Derivado, você vai se Derivado da versão substituída.

Se você precisa proteger a subclasse, você pode usar o padrão de delegado:

public class Protect extends Superclass {  // better implement an interface here
  private final Subclass subclass;

  public Protect(Subclass subclass) {
    this.subclass = subclass;
  }

  public void openMethod1(args) {
    this.subclass.openMethod1(args);
  }

  public int openMethod2(args) {
    return this.subclass.openMethod2(args);
  }
  ...
}

Você também pode pensar em usar java .lang.reflect.Proxy
[]]

resposta de Pedro e resposta de John estão corretas: basta lançar o tipo estático não fará nada se o tipo real do objeto (que o código do cliente pode lançar a, métodos de chamada de reflexão sobre, etc.) ainda está disponível. Você tem que disfarçar o tipo real de alguma forma (como resposta de Pedro menciona).

Há realmente um padrão para fazer isso: ter um olhar para os métodos unconfigurable* no Executors classe, se você tiver acesso ao código-fonte do JDK.

Java

Em Java, você nunca pode "Hide" algo de um objeto. O compilador pode esquecer o que você sabe em detalhes sobre o caso particular que você tem. (Como o que subclasse é) O depurador sabe muito mais do que o compilador, por isso vai lhe dizer o tipo real da instância atual é, o que é provavelmente o que você está experimentando.

OOP

Parece que você está lógica escrita que precisa saber o tipo de objeto que você está usando o que não é recomendado em OOP, mas muitas vezes necessária por razões práticas.

restringindo Adjetivos

Como eu disse em um comentário para a pergunta, você deve ser claro sobre o que é a classe base aqui. Intuitivamente RestrictedUser deve ser uma subclasse de Utilizador desde o nome sugere um tipo mais especializado. (Nome ter um adjetivo activos adicionais sobre ele.) No seu caso é especial, pois este é um adjetivo que limita o que permitiria que você coloca usuário como uma subclasse de RestrictedUser totalmente bem. Eu recomendaria renomear os dois para algo como:. BasicUser / UserWithId e NonRestrictedUser para evitar o adjetivo limitante que é a parte confundindo aqui

Além disso, não ser tentado a pensar que você pode esconder métodos em uma classe anônima quer. Por exemplo, este não irá fornecer a privacidade que você espera:

Runnable run = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }

    public void secretSquirrel() {
        System.out.println("Surprise!");
    }
}

Você pode não ser capaz de nomear o tipo real de run (a fim de lançar a ele diretamente), mas você ainda pode obter sua classe com getClass(), e você pode chamar secretSquirrel usando reflexão. (Mesmo se secretSquirrel é privado, se você não tem SecurityManager, ou se ele foi configurado para permitir a acessibilidade, a reflexão pode invocar métodos privados também.)

Eu acho que você provavelmente fez uma escolha de nomenclatura pobres:. Eu acho que RestrictedUser seria uma subclasse de User, não o contrário, então eu vou usar UnrestrictedUser como a subclasse

Se você upcast um UnrestrictedUser a um RestrictedUser, sua referência só será capaz de métodos de acesso declarou em RestrictedUser. No entanto, se você downcast-lo de volta a um UnrestrictedUser, você terá acesso a todos os métodos. Desde objetos Java saber que tipo elas realmente são, qualquer coisa que na verdade é um UnrestrictedUser sempre pode ser lançado de volta para ela, não importa que tipo de referência que você está usando (até mesmo um Object). Não é inevitável e parte da linguagem. No entanto, você pode escondê-lo atrás de algum tipo de proxy, mas no seu caso, que provavelmente vai contra o propósito.

Além disso, em Java, todos os métodos não-estáticos são virtuais por padrão. Isto significa que se UnrestrictedUser substitui algum método declarado em RestrictedUser, então a versão UnrestrictedUser será usada, mesmo se você acessá-lo através de uma referência RestrictedUser. Se você precisar chamar a versão classe pai, você pode fazer uma chamada não-virtual chamando RestrictedUser.variableName.someMethod(). No entanto, você provavelmente não deveria usar esse muita frequência (a menor das razões é que ele quebra o encapsulamento).

Eu também acho que esta questão levanta outra questão. Como você planeja para chamar esse método? Parece que ter uma hierarquia de classes, onde introduz Subclassing novos métodos assinaturas vai exigir verificações instanceof em chamadores.

Você pode atualizar sua pergunta para incluir um cenário onde você iria chamar esses métodos em questão? Talvez nós será capaz de ajudar mais.

A resposta técnica foi dado por John Calsbeek. Eu quero pensar sobre o problema de design. O que você está tentando fazer é impedir que algum segmento do código, ou alguns desenvolvedores, de saber nada sobre a classe de usuário. Você quer que eles para tratar todos os usuários como se fossem RestrictedUsers.

A maneira como você faria isso é com permissões. Você precisa impedir que os desenvolvedores em questão ter acesso à classe de usuário. Você provavelmente pode fazer isso, colocando-o em um pacote e restringindo o acesso ao pacote.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top