Por que eu não posso usar um bloco try em volta do meu super () chamar?

votos
34

Assim, em Java, a primeira linha de seu construtor tem que haver uma chamada para super ... seja implicitamente chamar super () ou chamando explicitamente outro construtor. O que eu quero saber é, por que não posso colocar um bloco try em torno de que?

Meu caso específico é que eu tenho uma classe de simulação para um teste. Não há construtor padrão, mas eu quero um para fazer os testes mais simples de ler. Eu também quero envolver as exceções lançadas a partir do construtor em um RuntimeException.

Então, o que eu quero fazer é eficazmente esta:

public class MyClassMock extends MyClass {
    public MyClassMock() {
        try {
            super(0);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // Mocked methods
}

Mas Java reclama que de super não é a primeira declaração.

Minha solução:

public class MyClassMock extends MyClass {
    public static MyClassMock construct() {
        try {
            return new MyClassMock();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public MyClassMock() throws Exception {
        super(0);
    }

    // Mocked methods
}

É esta a melhor solução? Por que Java não me deixa fazer o primeiro?


Meu melhor palpite quanto ao porquê é que Java não quer me deixar ter um objeto construído em um estado potencialmente inconsistente ... no entanto, em fazer um simulado, eu não me importo com isso. Parece que eu deveria ser capaz de fazer o acima ... ou pelo menos eu sei que o acima é seguro para o meu caso ... ou parece que ele deve ser de qualquer maneira.

Estou substituindo quaisquer métodos que eu uso a partir da classe testada, por isso não há risco de que estou usando variáveis ​​não inicializadas.

Publicado 07/08/2008 em 22:17
fonte usuário
Em outras línguas...                            


7 respostas

votos
14

Infelizmente, os compiladores não pode trabalhar em princípios teóricos, e mesmo que você saiba que é seguro no seu caso, se eles permitiram que ele, ele teria que ser seguro para todos os casos.

Em outras palavras, o compilador não está parando apenas você, ele está parando todos, incluindo todos aqueles que não sabem que ele é inseguro e precisa de tratamento especial. Há provavelmente outras razões para isso também, como todas as línguas geralmente têm maneiras de fazer inseguras coisas se sabe como lidar com eles.

Em C # .NET, existem disposições semelhantes, ea única maneira de declarar um construtor que chama um construtor base é esta:

public ClassName(...) : base(...)

ao fazê-lo, o construtor base será chamado antes do corpo do construtor, e você não pode alterar essa ordem.

Respondeu 07/08/2008 em 22:32
fonte usuário

votos
6

Eu sei que isto é uma questão de idade, mas eu gostei, e, como tal, eu decidi dar-lhe uma resposta minha. Talvez a minha compreensão de por que isso não pode ser feito vai contribuir para a discussão e para os futuros leitores da sua pergunta interessante.

Deixe-me começar com um exemplo de não construção do objeto.

Vamos definir uma classe A, tal que:

class A {
   private String a = "A";

   public A() throws Exception {
        throw new Exception();
   }
}

Agora, vamos supor que gostaria de criar um objeto do tipo A em um try...catchbloco.

A a = null;
try{
  a = new A();
}catch(Exception e) {
  //...
}
System.out.println(a);

Evidentemente, a saída desse código será: null.

Por que Java não retornar uma versão parcialmente construído de A? Afinal, pelo ponto do construtor falhar, o objeto do namecampo já foi inicializado, certo?

Bem, Java não pode retornar uma versão parcialmente construído de Aporque o objeto não foi construída com sucesso. O objeto está em um estado inconsistente, e é, portanto, descartado pelo Java. Seu variável A não é sequer inicializado, ele é mantido como nulo.

Agora, como você sabe, construir plenamente um novo objeto, todos os seus super-classes devem ser inicializado pela primeira vez. Se uma das classes de super não conseguiu executar, o que seria o estado final do objeto? É impossível determinar isso.

Veja este exemplo mais elaborado

class A {
   private final int a;
   public A() throws Exception { 
      a = 10;
   }
}

class B extends A {
   private final int b;
   public B() throws Exception {
       methodThatThrowsException(); 
       b = 20;
   }
}

class C extends B {
   public C() throws Exception { super(); }
}

Quando o construtor Cé invocado, se ocorrer uma exceção durante a inicialização B, o que seria o valor da última intvariável b?

Como tal, o objecto de C não pode ser criado, é falso, que é lixo, não está totalmente inicializado.

Para mim, isso explica por que seu código é ilegal.

Respondeu 21/04/2012 em 18:09
fonte usuário

votos
5

Ele é feito para impedir que alguém criar um novo SecurityManagerobjeto a partir do código não confiável.

public class Evil : SecurityManager {
  Evil()
  {
      try {
         super();
      } catch { Throwable t }
      {
      }
   }
}
Respondeu 16/09/2008 em 21:55
fonte usuário

votos
2

Eu não posso presumir de ter uma compreensão profunda de internos Java, mas é meu entendimento que, quando um compilador precisa instanciar uma classe derivada, tem que primeiro criar a base (e sua base antes que (...)) e depois tapa nas extensões feitas na subclasse.

Portanto, não é mesmo o perigo de variáveis uninited ou qualquer coisa assim em tudo. Quando você tenta fazer algo na subclasse construtor antes de a classe base construtor , você está basicamente pedindo o compilador para estender uma instância do objeto base que ainda não existe.

Edit: No seu caso, MyClass se torna o objeto base, e MyClassMock é uma subclasse.

Respondeu 07/08/2008 em 22:43
fonte usuário

votos
2

Eu não sei como Java é implementado internamente, mas se o construtor da superclasse lança uma exceção, então não é uma instância da classe que você estender. Seria impossível para chamar os toString()ou equals()métodos, por exemplo, uma vez que eles são herdados na maioria dos casos.

Java pode permitir que um try / catch em torno da chamada super () no construtor se 1. você substituir todos os métodos das superclasses, e 2. você não usar a cláusula super.XXX (), mas isso tudo soa muito complicado para mim.

Respondeu 07/08/2008 em 22:41
fonte usuário

votos
0

Eu sei que esta questão tem inúmeras respostas, mas eu gostaria de dar o meu pequeno petisco sobre por que isso não seria permitido, especificamente para responder por que Java não permite que você faça isso. Então aqui vai ...

Agora, tenha em mente que super()tem de ser chamado antes de qualquer outra coisa no construtor de uma subclasse, por isso, se você usou trye catchblocos em torno de sua super()chamada, os blocos teria que ficar assim:

try {
   super();
   ...
} catch (Exception e) {
   super(); //This line will throw the same error...
   ...
}

Se super () fails in thetentar block, it HAS to be executed first in thecaptura block, so thatde super runs before anything in your subclassconstrutor s. Isto deixa-o com o mesmo problema que você teve no início: se uma exceção é lançada, não está preso. (Neste caso, ele só fica jogado novamente no bloco catch.)

Agora, o código acima não é de forma permitido pelo Java também. Este código pode executar metade da primeira chamada super, e então chamá-lo de novo, o que poderia causar alguns problemas com algumas classes de super.

Agora, a razão que o Java não permite que você lançar uma exceção em vez da chamada super()é porque a exceção poderia ser pego em outro lugar, e o programa continuaria sem chamar super()em seu objeto de subclasse, e, possivelmente, porque a exceção poderia ter o seu objeto como um parâmetro e tentar alterar o valor de variáveis de instância herdadas, que não ainda foram inicializados.

Respondeu 17/04/2017 em 15:53
fonte usuário

votos
-1

Uma maneira de contornar isso é chamando uma função estática privada. A tentativa-captura pode, então, ser colocado no corpo da função.

public class Test  {
  public Test()  {
     this(Test.getObjectThatMightThrowException());
  }
  public Test(Object o)  {
     //...
  }
  private static final Object getObjectThatMightThrowException()  {
     try  {
        return  new ObjectThatMightThrowAnException();
     }  catch(RuntimeException rtx)  {
        throw  new RuntimeException("It threw an exception!!!", rtx);
     }
  }
}
Respondeu 28/03/2014 em 20:35
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more