Acesso protected a classes internas

18 respostas
Bruno_Cardoso

Boas,

Estou a ler a 3ª edição do livro "Thinking in Java" mas agora fiquei preso num dos exercicios que é dado para fazer.. resolvi por aqui para ver se alguem me pode dizer se isto é mesmo possivel ou se foi um erro do autor, é q eu não estou mesmo a ver como o fazer

Este é o exercicio:

Create an interface with at least one method, in its own package. Create a class in a separate package. Add a protected inner class that implements the interface. In a third package, inherit from your class and, inside a method, return an object of the protected inner class, upcasting to the interface during the return.

Agora o código que fiz:
package c08.ex12.mypackage;

public interface A {
	void a1();
}
package c08.ex12.myclass;
import c08.ex12.mypackage.A;

public class B {
	protected class C implements A {
		public void a1() {}
	}
}
package c08.ex12.main;
import c08.ex12.myclass.*;
import c08.ex12.mypackage.*;

public class Teste extends B {
	A method() {
		B b = new B();
		return (A)b.new C(); 
	}
}

O erro está em return (A)b.new C();, não me deixa criar um objecto porque a classe interna C não está visivel por ser protected.. sei que se mudar para public funciona mas n é essa a ideia, alguem me pode ajudar e esclarecer ?

Obrigado.

18 Respostas

Rafael_Steil

O modificador “protected” eh a coisa mais bizarra que existe no Java. Tem cada coisa sinistra no funcionamento dele que chega a assustar…
Se voce declarar o construtor como public ( nao a classe, mas apenas o construtor ), entao vc pode ter acesso sem problemas. O mais estranho nesse caso eh que voce pode ter entao acesso mesmo sem extender a classe que contem a inner class, o que violaria as regras.

Sinistro, e a Java Language Specification eh meio confusa em relacao a isso…
mais info ( ou correcoes ) no decorrer do periodo.

Rafael

Paulo_Silveira

ateh faz sentido nao funcionar. porque o protected, fora do meesmo pacote, voce nao pode acessar esse membro/classe interna de outra instancia, voce herda, mas nao acessa.

agora, o seguinte codigo DEVERIA funcionar:

mude o terceiro codigo para o seguinte

package c08.ex12.main; 
import c08.ex12.myclass.*; 
import c08.ex12.mypackage.*; 

public class Teste extends c08.ex12.myclass.B { 
   A method() { 
      return (A) this.new C(); 
   } 
}

O Rafael compilou mas disse que ainda reclama do protected la de cima. Parece que o construtor default dela ta protected tambem, internamente a classe. Nao faz muito sentido.

O que ele coloca na resposta?

Rafael_Steil

Voce consgue acessar um membro protected via heranca, o que nao consegue eh acessar via instancia:

// b/ClassePai.java
package b;

public class ClassePai {
    protected int a;
}

// c/Teste.java
package c;

import b.*;

public class Teste extends ClassePai
{
    public void seila()
    {
        // consegumos acessar 'a' via heranca
        a = 1;

        // Nao conseguimos via instancia
        ClassePai p = new ClassePai();
        p.a = 5;
    }
}

resumindo: deverimos poder acessar a inner classe tambem.

Rafael

Bruno_Cardoso
Paulo, experimentei como disse e também não dá, continua a dizer que o constructor não é visivel.. já tentei de todas as maneiras.. o mais estranho é que o autor coloca no livro o seguinte exemplo:
class Parcel3 {
  private class PContents implements Contents {
    private int i = 11;
    public int value() { return i; }
  }
  protected class PDestination implements Destination {
    private String label;
    private PDestination(String whereTo) {
      label = whereTo;
    }
    public String readLabel() { return label; }
  }
  public Destination dest(String s) {
    return new PDestination(s);
  }
  public Contents cont() {
    return new PContents();
  }
}

public class TestParcel {
  public static void main(String[] args) {
    Parcel3 p = new Parcel3();
    Contents c = p.cont();
    Destination d = p.dest("Tanzania");
  }
}

E depois diz:

PDestination is protected, so no one but Parcel3, classes in the same package (since protected also gives package access), and the inheritors of Parcel3 can access PDestination. This means that the client programmer has restricted knowledge and access to these members.

Então se Teste é uma classe derivada de B, segundo o que o autor diz, eu deveria ter acesso às classes internas de Bmesmo tendo elas acesso protected, certo?

Obrigado aos dois pela ajuda,

PEACE!

dukejeffrie

Será que ele não quis dizer isso?

return new B.C();

[]s!

Rafael_Steil

Isso nao funciona tambem, pelos mesmos motivos anteriores… :-

Rafael

Bani

O enunciado da questão é “return an object of the protected inner class”. Não está especificado que tenha que ser um “novo” objeto da inner class…
Então, considerando que você consegue acessar através de herança, minha sugestão seria retornar um objeto tipo um singleton.

Rafael_Steil

Bom, com base nisso nem precisa ser singleton… poderia ter um metodo public da classe pai q retornasse o objeto:

// B.java ... public A newCInstance() { new return C(); } ]

e entao usar o newCInstance() pra pegar o objeto.

Mas mesmo assim: por que diabos eh possivel acessar um membro protected e nao eh possivel acessar a inner class protected??

Rafael

Bruno_Cardoso

Como o Rafael diz funciona mas eu acho que a ideia seria fazer um objecto da inner class C sem ter que acrescentar mais código… e daí posso estar enganado… não sei o que ia na cabeça do autor quando escreveu este exercicio, e quanto às soluções do mesmo só comprando o livro :stuck_out_tongue:

Já agora… o que é um “objecto tipo singleton” ?

PEACE!

Rafael_Steil

Da uma olhada neste link:

http://www.guj.com.br/forum/viewtopic.php?t=1535&highlight=singleton

Rafael

Bruno_Cardoso

Kewl! Ainda hoje li sobre esta tecnica aqui no manual, mas só agora é que fiquei a saber que é um pattern conhecido como singleton!

Bem, mais uma vez obrigado pessoal.

PEACE!

Bani

Rafael,

Como você mesmo disse na sua primeira mensagem, é possível acessar a inner class sim! Apenas o construtor da inner class que não dá para acessar.
Declarando o construtor público, é possível instanciar a inner class.

Porém aí você disse que com o construtor público teria acesso mesmo sem estender a outer class, mas isso já não é verdade. Se você não estende a outer class, seus membros ficam “private” para a classe que está chamando, e portanto você nem encherga ela desta outra classe, dando erro de “cannot resolve symbol” quando você tenta compilar.

Bani

E sobre porque tem que declarar o construtor público, a razão é simples: se você não coloca nenhum construtor para a sua classe, o compilador cria um construtor para você.
E esse construtor é sempre um simples
NomeDaClasse() { super(); }
Portanto, ele tem o acesso “default” e não é visível de fora do pacote.

Rafael_Steil

Eh verdade sim:

// b/ClassePai.java
package b;
import a.*;

public class ClassePai {
    protected int a;

    protected class Inner implements I {
        public Inner() {}
        public void a() {}
    }
}

// c/Teste.java
package c;

import a.*;
import b.*;

public class Teste
{
    public I seila()
    {
        return new ClassePai().new Inner();
    }
}

Se voce tenta isso com membros protected ( acesso sem extender ), da erro. Mas com a inner class nao. ( BEM o contrario do que acontecer quando voce extende e nao tem o construtor public ).

Rafael

Paulo_Silveira

“Bani”:
E sobre porque tem que declarar o construtor público, a razão é simples: se você não coloca nenhum construtor para a sua classe, o compilador cria um construtor para você.
E esse construtor é sempre um simples
NomeDaClasse() { super(); }
Portanto, ele tem o acesso “default” e não é visível de fora do pacote.

Isso nao eh verdade. Pode ateh ser para classe itnerna, mas para classe normal (nao interna, nao nested) o construtor EH public

acabei de fazer o teste novamente para nao falar besteira.
mas o que voce disse PARECE fazer sentido para classe interna

Bani

Realmente falei besteira sobre o construtor.
Após as verificações no JLS do Paulo e Rafael concluiu-se que o construtor fica com o mesmo modificador de acesso da classe.
Mas a idéia básica ainda está valendo: o construtor é protected, portanto só tem acesso por herança, e a classe que estamos estendendo não é a inner class, e sim a outer.

Bani

E sobre o novo código do Rafael, aparentemente depende da versão do compilador…
O meu está dando erro:

C:>javac c08cTeste.java c08cTeste.java:10: b.ClassePai.Inner has protected access in b.ClassePai return new ClassePai().new Inner(); ^ c08cTeste.java:10: Inner() in b.ClassePai.Inner is not defined in a public class or interface; cannot be accessed from outside package return new ClassePai().new Inner(); ^ 2 errors

Rafael_Steil

Eu testei com dois compiladores diferentes… nao pode ser o compilador, deve ser alguma outra coisa…
Para quem quiser testar, o codigo esta em

http://www.insanecorp.com/teste_protected.tar.gz

editado: apos a bani ter reportado que o codigo ainda dava pau, limpei tudo. De fato, com o javac DA pau, mas com o Jikes, da IBM, NAO… e agora?

Rafael

Criado 25 de fevereiro de 2003
Ultima resposta 25 de fev. de 2003
Respostas 18
Participantes 5