Erro de Java: o superconstrutor implícito é indefinido para o construtor padrão

88

Eu tenho um código Java simples que se parece com este em sua estrutura:

abstract public class BaseClass {
    String someString;
    public BaseClass(String someString) {
        this.someString = someString;
    }
    abstract public String getName();
}

public class ACSubClass extends BaseClass {
    public ASubClass(String someString) {
        super(someString);
    }
    public String getName() {
        return "name value for ASubClass";
    }
}

Terei algumas subclasses de BaseClass, cada uma implementando o getName()método em sua própria maneira ( padrão de método de modelo ).

Isso funciona bem, mas não gosto de ter o construtor redundante nas subclasses. É mais para digitar e é difícil de manter. Se eu fosse alterar a assinatura do método do BaseClassconstrutor, teria que alterar todas as subclasses.

Quando removo o construtor das subclasses, recebo este erro em tempo de compilação:

Implicit super constructor BaseClass() is undefined for default constructor. Must define an explicit constructor

O que estou tentando fazer é possível?

Joel
fonte
1
Por favor, deixe o construtor 'redundante'! Ele mantém a legibilidade do seu código e todos os IDEs modernos podem criá-lo automaticamente, então você só precisa digitar um atalho.
Andreas Dolk
3
Relendo minha própria pergunta um ano depois, me ocorre que poderia ter removido os construtores (incl na classe base) como matt b sugerido, e então usar um método de fábrica estático para construir instâncias.
Joel

Respostas:

145

Você obtém este erro porque uma classe que não tem construtor tem um construtor padrão , que não tem argumentos e é equivalente ao seguinte código:

public ACSubClass() {
    super();
}

No entanto, como sua BaseClass declara um construtor (e, portanto, não tem o construtor padrão, sem argumentos que o compilador forneceria), isso é ilegal - uma classe que estende BaseClass não pode ser chamada super();porque não há um construtor sem argumento em BaseClass.

Isso provavelmente é um pouco contra-intuitivo, porque você pode pensar que uma subclasse possui automaticamente qualquer construtor que a classe base possui.

A maneira mais simples de contornar isso é a classe base não declarar um construtor (e, portanto, ter o construtor não-arg padrão) ou ter um construtor não-arg declarado (sozinho ou ao lado de qualquer outro construtor). Mas muitas vezes essa abordagem não pode ser aplicada - porque você precisa de todos os argumentos que estão sendo passados ​​para o construtor para construir uma instância legítima da classe.

matt b
fonte
17
"Isso provavelmente é um pouco contra-intuitivo, porque você pode pensar que uma subclasse possui automaticamente qualquer construtor que a classe base possui." +1
Mr_and_Mrs_D
2
Para o bem da posteridade, vou sugerir minha solução para futuros leitores: crie um construtor sem argumentos em, BaseClassmas faça-o simplesmente lançar um UnsupportedOperationExceptionou algo assim. Não é a melhor solução (sugere falsamente que a classe pode suportar um construtor sem arg), mas é a melhor que posso pensar.
JMTyler
49

Para quem procura esse erro no Google e chega aqui: pode haver outro motivo para recebê-lo. O Eclipse apresenta esse erro quando você tem configuração do projeto - incompatibilidade de configuração do sistema.

Por exemplo, se você importar o projeto Java 1.7 para o Eclipse e não tiver o 1.7 configurado corretamente, receberá este erro. Então você pode ir para Project - Preference - Java - Compilere switch to 1.6 or earlier; ou vá para Window - Preferences - Java - Installed JREse adicione / corrija a instalação do JRE 1.7.

MF.OX
fonte
2
Acabei de receber esse erro sem motivo aparente no Eclipse. Então limpei o espaço de trabalho (menu Projeto -> Limpar ...) e ele foi embora.
erickrf
7

É possível, mas não do jeito que você o tem.

Você tem que adicionar um construtor no-args à classe base e é isso!

public abstract class A {
    private String name;
    public A(){
        this.name = getName();
    }
    public abstract String getName();


    public String toString(){
        return "simple class name: " + this.getClass().getSimpleName() + " name:\"" + this.name + "\"";
    }
}
class B extends A {
    public String getName(){
        return "my name is B";
    }
    public static void main( String [] args ) {
        System.out.println( new C() );
    }
}
class C extends A {
    public String getName() {
        return "Zee";
    }
}

Quando você não adiciona um construtor (qualquer) a uma classe, o compilador adiciona o construtor sem arg padrão para você.

Quando o padrão nenhum arg chama super (); e como você não o tem na superclasse, você recebe aquela mensagem de erro.

Isso é sobre a própria questão.

Agora, expandindo a resposta:

Você está ciente de que criar uma subclasse (comportamento) para especificar um valor diferente (dados) não faz sentido ?? !!! Espero que você faça.

Se a única coisa que muda é o "nome", então uma única classe parametrizada é o suficiente!

Então você não precisa disso:

MyClass a = new A("A");
MyClass b = new B("B");
MyClass c = new C("C");
MyClass d = new D("D");

ou

MyClass a = new A(); // internally setting "A" "B", "C" etc.
MyClass b = new B();
MyClass c = new C();
MyClass d = new D();

Quando você pode escrever isto:

MyClass a = new MyClass("A");
MyClass b = new MyClass("B");
MyClass c = new MyClass("C");
MyClass d = new MyClass("D");

Se eu fosse alterar a assinatura do método do construtor BaseClass, teria que alterar todas as subclasses.

Bem, é por isso que a herança é o artefato que cria o acoplamento HIGH, o que é indesejável em sistemas OO. Deve ser evitado e talvez substituído por composição.

Pense se você realmente precisa deles como subclasse. É por isso que você vê muitas vezes interfaces usadas em vez de:

 public interface NameAware {
     public String getName();
 }



 class A implements NameAware ...
 class B implements NameAware ...
 class C ... etc. 

Aqui B e C poderiam ter herdado de A, o que teria criado um acoplamento muito ALTO entre eles, ao usar interfaces o acoplamento é reduzido, se A decidir que não será mais "NameAware" as outras classes não quebrarão.

Claro, se você quiser reutilizar o comportamento, isso não funcionará.

OscarRyz
fonte
2
Sim, exceto que você não pode mais garantir que suas instâncias sejam inicializadas corretamente (por exemplo, têm nomes neste caso específico)
ChssPly76
@ ChssPly76: Sim, mas provavelmente é porque a herança está sendo usada de maneira inadequada. Eu expandi minha resposta para cobri-lo.
OscarRyz
4

Você também pode obter este erro quando o JRE não está definido. Nesse caso, tente adicionar a Biblioteca do sistema JRE ao seu projeto.

Sob Eclipse IDE:

  1. abra o menu Projeto -> Propriedades ou clique com o botão direito em seu projeto no Package Explorer e escolha Propriedades (Alt + Enter no Windows, Command + I no Mac)
  2. clique em Java Build Path e na guia Libraries
  3. escolher ModulePath ou Classpath e pressione Add Biblioteca ... botão
  4. selecione JRE System Library e clique em Next
  5. mantenha o JRE padrão do espaço de trabalho selecionado (você também pode escolher outra opção) e clique em Concluir
  6. por fim, pressione Aplicar e Fechar .
Yogesh Kumar
fonte
2

Outra maneira é chamar super () com o argumento necessário como uma primeira instrução no construtor da classe derivada.

public class Sup {
    public Sup(String s) { ...}
}

public class Sub extends Sup {
    public Sub() { super("hello"); .. }
}
user2407102
fonte
0

O Eclipse apresentará esse erro se você não tiver uma chamada para o construtor da superclasse como uma primeira instrução no construtor da subclasse.

Sagar
fonte
0

Desculpe por necropostar, mas enfrentei esse problema apenas hoje. Para todos que também enfrentam esse problema - uma das possíveis razões - você não chama superna primeira linha do método. A segunda, a terceira e outras linhas disparam esse erro. A chamada de super deve ser a primeira chamada em seu método. Neste caso está tudo bem.

Serj.by
fonte
-1

Você pode resolver esse erro adicionando um construtor sem argumentos à classe base (conforme mostrado abaixo).

Felicidades.

 abstract public class BaseClass {
        // ADD AN ARGUMENTLESS CONSTRUCTOR TO THE BASE CLASS
        public BaseClass(){
        }

        String someString;
        public BaseClass(String someString) {
            this.someString = someString;
        }
        abstract public String getName();
    }

public class ACSubClass extends BaseClass {
    public ASubClass(String someString) {
        super(someString);
    }
    public String getName() {
        return "name value for ASubClass";
    }
}
BrainO2
fonte
Isso torna mais fácil construir objetos inválidos (aqueles sem someString) definidos e, portanto, anula totalmente o propósito de como construtor.
Robert
-1

Eu tive esse erro e o corrigi removendo uma exceção lançada ao lado do método para um bloco try / catch

Por exemplo: DE:

public static HashMap<String, String> getMap() throws SQLException
{

}

PARA:

public static Hashmap<String,String> getMap()
{
  try{

  }catch(SQLException)
  { 
  }
}
Thomas Johnson
fonte
Essa resposta não tem nada a ver com erros do compilador para um construtor ausente.
Robert