Métodos opcionais na interface Java

120

Do meu entendimento, se você implementar uma interface em java, os métodos especificados nessa interface devem ser usados ​​pelas subclasses que implementam a referida interface.

Percebi que em algumas interfaces, como a interface Collection, existem métodos comentados como opcionais, mas o que isso significa exatamente? Isso me surpreendeu um pouco, pois pensei que todos os métodos especificados na interface seriam necessários?

mjsey
fonte
A quais métodos você está se referindo? Não consigo encontrar no JavaDoc ou no código-fonte
dcpomero
possível duplicata de O que significa "operação opcional" em Javadoc de, por exemplo, Set # add (E)?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respostas:

232

Parece haver uma grande confusão nas respostas aqui.

A linguagem Java requer que todos os métodos em uma interface sejam implementados por todas as implementações dessa interface. Período. Não há exceções a esta regra. Dizer "Coleções são uma exceção" sugere uma compreensão muito confusa do que realmente está acontecendo aqui.

É importante perceber que existem dois níveis de conformidade com uma interface:

  1. O que a linguagem Java pode verificar. Isso basicamente se resume a: há alguma implementação para cada um dos métodos?

  2. Cumprindo o contrato de fato. Ou seja, a implementação faz o que a documentação na interface diz que deveria?

    Interfaces bem escritas incluirão documentação explicando exatamente o que se espera das implementações. Seu compilador não pode verificar isso para você. Você precisa ler os documentos e fazer o que eles dizem. Se você não fizer o que o contrato diz, você terá uma implementação da interface no que diz respeito ao compilador , mas será uma implementação defeituosa / inválida.

Ao projetar a API de coleções, Joshua Bloch decidiu que, em vez de ter interfaces muito refinadas para distinguir entre diferentes variantes de coleções (por exemplo: legível, gravável, acesso aleatório, etc.), ele teria apenas um conjunto muito grosso de interfaces, principalmente Collection, List, Sete Map, em seguida, documentar certas operações como "opcional". Isso era para evitar a explosão combinatória que resultaria de interfaces refinadas. Das Perguntas frequentes sobre o design da API de coleções Java :

Para ilustrar o problema em detalhes sangrentos, suponha que você queira adicionar a noção de modificabilidade à Hierarquia. Você precisa de quatro novas interfaces: ModifiableCollection, ModifiableSet, ModifiableList e ModifiableMap. O que antes era uma hierarquia simples, agora é uma heterarquia confusa. Além disso, você precisa de uma nova interface Iterator para uso com Coleções não modificáveis, que não contenha a operação de remoção. Agora você pode acabar com UnsupportedOperationException? Infelizmente não.

Considere matrizes. Eles implementam a maioria das operações List, mas não removem e adicionam. São listas de "tamanho fixo". Se você quiser capturar essa noção na hierarquia, deve adicionar duas novas interfaces: VariableSizeList e VariableSizeMap. Você não tem que adicionar VariableSizeCollection e VariableSizeSet, porque eles seriam idênticos a ModifiableCollection e ModifiableSet, mas você pode escolher adicioná-los de qualquer maneira por uma questão de consistência. Além disso, você precisa de uma nova variedade de ListIterator que não ofereça suporte às operações de adição e remoção, para acompanhar a lista não modificável. Agora temos até dez ou doze interfaces, além de duas novas interfaces Iterator, em vez de nossas quatro originais. Nós terminamos? Não.

Considere os logs (como logs de erros, logs de auditoria e diários para objetos de dados recuperáveis). Elas são sequências naturais apenas de acréscimo, que oferecem suporte a todas as operações List, exceto para remover e definir (substituir). Eles exigem uma nova interface principal e um novo iterador.

E quanto às coleções imutáveis, em oposição às não modificáveis? (ou seja, coleções que não podem ser alteradas pelo cliente E nunca serão alteradas por qualquer outro motivo). Muitos argumentam que esta é a distinção mais importante de todas, porque permite que vários threads acessem uma coleção simultaneamente sem a necessidade de sincronização. Adicionar este suporte à hierarquia de tipo requer mais quatro interfaces.

Agora temos cerca de vinte interfaces e cinco iteradores, e é quase certo que ainda existam coleções surgindo na prática que não se encaixam perfeitamente em nenhuma das interfaces. Por exemplo, as visualizações de coleção retornadas por Map são coleções naturais apenas para exclusão. Além disso, existem coleções que rejeitarão certos elementos com base em seu valor, então ainda não eliminamos as exceções de tempo de execução.

Quando tudo foi dito e feito, sentimos que era um compromisso sólido da engenharia contornar todo o problema, fornecendo um conjunto muito pequeno de interfaces centrais que podem lançar uma exceção de tempo de execução.

Quando os métodos na API de coleções são documentados como sendo "operações opcionais", isso não significa que você pode simplesmente deixar a implementação do método de fora na implementação, nem significa que você pode usar um corpo de método vazio (por um lado, muitos dos eles precisam retornar um resultado). Em vez disso, significa que uma escolha de implementação válida (aquela que ainda está em conformidade com o contrato) é lançar um UnsupportedOperationException.

Observe que porque UnsupportedOperationExceptioné um, RuntimeExceptionvocê pode lançá-lo a partir de qualquer implementação de método, no que diz respeito ao compilador. Por exemplo, você pode jogá-lo de uma implementação de Collection.size(). No entanto, tal implementação violaria o contrato, pois a documentação para Collection.size()não diz que isso é permitido.

À parte: a abordagem usada pela API de coleções do Java é um tanto controversa (provavelmente menos agora do que quando foi introduzida pela primeira vez, no entanto). Em um mundo perfeito, as interfaces não teriam operações opcionais e, em vez disso, seriam usadas interfaces refinadas. O problema é que o Java não suporta nem tipos estruturais inferidos ou tipos de interseção, e é por isso que tentar fazer as coisas da "maneira certa" acaba se tornando extremamente difícil no caso de coleções.

Laurence Gonsalves
fonte
30
1 para There are no exceptions to this rule. Quer saber por que essa resposta não foi marcada como aceita. Outros são bons, mas você deu mais do que o suficiente.
xyz
9
"A linguagem Java requer que cada método em uma interface seja implementado por cada implementação dessa interface. Ponto final. Não há exceções a esta regra." Exceto ... quando há. :-) As interfaces Java 8 podem especificar uma implementação de método padrão, portanto, em Java 8 ... NÃO é verdade que todo método em uma interface deve ser IMPLEMENTADO por cada implementação da interface, pelo menos não no sentido que você deve codifique a implementação na classe de concreto.
DaBlick
1
@DaBlick Quando eu disse "é implementado por todas as implementações", não quis dizer que a implementação do método deve residir na origem da classe de implementação. Mesmo antes do Java 8, pode-se herdar uma implementação de um método de interface, mesmo de uma classe que não implementa essa interface. Ex: criar Fooque não implemente Runnablecom método público void run(). Agora crie uma classe Barque extends Fooe implements Runnablesem exagerar run. Ele ainda implementa o método, embora indiretamente. Da mesma forma, uma implementação de método padrão ainda é uma implementação.
Laurence Gonsalves,
Desculpas. Eu não estava tentando ser muito crítico, mas sim chamar a atenção para um recurso do Java 8 que pode ser relevante para o post original. No Java 8, agora você tem a opção de ter implementações que não são codificadas em NENHUMA superclasse ou subclasse. Isso (IMHO) abre um novo mundo de padrões de projeto, incluindo alguns que podem ser apropriados nos casos em que a proibição de herança múltipla pode ter apresentado alguns desafios. Acho que isso vai gerar um novo conjunto de padrões de design altamente úteis. \
DaBlick
3
@AndrewS porque no Java 8 removefoi dada uma implementação padrão. Se você não implementá-lo, sua classe obterá a implementação padrão. Os outros dois métodos que você mencionou não têm implementações padrão.
Laurence Gonsalves,
27

Para compilar uma classe de implementação (não abstrata) para uma interface - todos os métodos devem ser implementados.

No entanto , se pensarmos em um método em que sua implementação é uma simples exceção lançada como um 'não implementado' (como alguns métodos na Collectioninterface), então a Collectioninterface é a exceção neste caso, não o caso regular. Normalmente , a classe de implementação deve (e irá) implementar todos os métodos.

O "opcional" na coleção significa que a classe de implementação não precisa 'implementá-la' (de acordo com a terminologia acima), e ela apenas lançará NotSupportedException).

Um bom exemplo - add()método para coleções imutáveis ​​- o concreto irá apenas implementar um método que não faz nada além de lançarNotSupportedException

No caso de Collectionser feito para evitar árvores de herança confusas, isso deixará os programadores infelizes - mas para a maioria dos casos, esse paradigma não é recomendado e deve ser evitado se possível.


Atualizar:

A partir do java 8, um método padrão foi introduzido.

Isso significa que uma interface pode definir um método - incluindo sua implementação.
Isso foi adicionado para permitir a adição de funcionalidade às interfaces, ao mesmo tempo em que oferece suporte à compatibilidade com versões anteriores para partes de código que não precisam da nova funcionalidade.

Observe que o método ainda é implementado por todas as classes que o declaram, mas usando a definição da interface.

amit
fonte
Em vez de "não bagunçar", acho que é mais "é assim mesmo".
@pst: Eu acredito que o que os designers estavam pensando ao implementá-lo em primeiro lugar, mas não tenho como saber ao certo. Acho que qualquer abordagem diferente criaria uma bagunça, mas, novamente - pode estar errada. O que eu estava tentando mostrar aqui é: este exemplo é a exceção, não o usual - e embora às vezes possa ser útil - para o caso geral - deve ser evitado, se possível.
Amit
12
"não tem que implementá-lo (Provavelmente irá apenas criar um método que lance ...)". Isso é implementar o método.
Marquês de Lorne
1
Como esta infelizmente foi a resposta aceita, sugiro reescrevê-la. O ' Normalmente , a classe de implementação deve (e irá) implementar todos os métodos' é enganosa, como o EJP já apontou.
Alberto
2
O "opcional" na coleção significa que a classe de implementação não precisa implementá-la. "- isso é totalmente falso. Por" não precisa implementar "você quer dizer outra coisa.
djechlin
19

Uma interface em Java apenas declara o contrato para implementação de classes. Todos os métodos nessa interface devem ser implementados, mas as classes de implementação são livres para deixá-los não implementados, viz., Em branco. Como um exemplo inventado,

interface Foo {
  void doSomething();
  void doSomethingElse();
}

class MyClass implements Foo {
  public void doSomething() {
     /* All of my code goes here */
  }

  public void doSomethingElse() {
    // I leave this unimplemented
  }
}

Agora deixei doSomethingElse()sem implementação, deixando-o livre para implementação de minhas subclasses. Isso é opcional.

class SubClass extends MyClass {
    @Override
    public void doSomethingElse() {
      // Here's my implementation. 
    }
}

No entanto, se você está falando sobre interfaces de coleção, como outros já disseram, elas são uma exceção. Se certos métodos não forem implementados e você os chamar, eles podem lançar UnsupportedOperationExceptionexceções.

SRI
fonte
Eu poderia te beijar meu amigo.
Micro de
16

Os métodos opcionais na interface Collection significam que a implementação do método pode lançar uma exceção, mas deve ser implementado de qualquer maneira. Conforme especificado nos documentos :

Algumas implementações de coleção têm restrições sobre os elementos que podem conter. Por exemplo, algumas implementações proíbem elementos nulos e algumas têm restrições sobre os tipos de seus elementos. A tentativa de adicionar um elemento inelegível lança uma exceção não verificada, normalmente NullPointerException ou ClassCastException. Tentar consultar a presença de um elemento inelegível pode lançar uma exceção ou pode simplesmente retornar falso; algumas implementações exibirão o primeiro comportamento e algumas exibirão o último. Mais geralmente, a tentativa de uma operação em um elemento inelegível cuja conclusão não resultaria na inserção de um elemento inelegível na coleção pode lançar uma exceção ou pode ter sucesso, por opção da implementação. Essas exceções são marcadas como "opcionais"

MByD
fonte
Eu nunca entendi realmente o que os javadocs querem dizer com opcional. Eu acredito que eles quiseram dizer como você disse. Mas a maioria dos métodos são opcionais por esse padrão new Runnable ( ) { @ Override public void run ( ) { throw new UnsupportedOperationException ( ) ; } }:;
emory
Isso não parece se aplicar a métodos opcionais , mas sim que, por exemplo, add((T)null)pode ser válido em um caso, mas não em outro. Ou seja, fala sobre exceções / comportamento opcionais e para argumentos ("restrições de elementos" ... "exceções de elemento inelegível" ... "marcadas como opcionais") e não aborda métodos opcionais .
9

Todos os métodos devem ser implementados para que o código seja compilado (exceto aqueles com defaultimplementações em Java 8+), mas a implementação não precisa fazer nada funcionalmente útil. Especificamente, ele:

  • Pode estar em branco (um método vazio).
  • Pode apenas lançar um UnsupportedOperationException(ou similar)

A última abordagem é freqüentemente usada nas classes de coleção - todos os métodos ainda são implementados, mas alguns podem lançar uma exceção se chamados em tempo de execução.

Michael Berry
fonte
5

Na verdade, estou inspirado por SurfaceView.Callback2. Eu acho que esta é a forma oficial

public class Foo {
    public interface Callback {
        public void requiredMethod1();
        public void requiredMethod2();
    }

    public interface CallbackExtended extends Callback {
        public void optionalMethod1();
        public void optionalMethod2();
    }

    private Callback mCallback;
}

Se sua classe não precisa implementar métodos opcionais, apenas "implementa Callback". Se sua classe precisa implementar métodos opcionais, apenas "implementa CallbackExtended".

Desculpe pelo inglês de merda.

Wonson
fonte
5

No Java 8 e posterior, a resposta a esta pergunta ainda é válida, mas agora é mais matizada.

Primeiro, essas afirmações da resposta aceita permanecem corretas:

  • interfaces destinam-se a especificar seus comportamentos implícitos em um contrato (uma declaração de regras para o comportamento que as classes de implementação devem obedecer para serem consideradas válidas)
  • há uma distinção entre o contrato (regras) e a implementação (codificação programática das regras)
  • métodos especificados na interface SEMPRE DEVEM ser implementados (em algum ponto)

Então, qual é a nuance que há de novo no Java 8? Ao falar de "Métodos Opcionais", qualquer um dos seguintes agora é adequado:

1. Um método cuja implementação é contratualmente opcional

A "terceira declaração" diz que os métodos abstratos de interface devem sempre ser implementados e isso permanece verdadeiro no Java 8+. No entanto, como no Java Collections Framework, é possível descrever alguns métodos abstratos de interface como "opcionais" no contrato.

Nesse caso, o autor que está implementando a interface pode optar por não implementar o método. O compilador irá insistir em uma implementação, entretanto, e então o autor usa este código para quaisquer métodos opcionais que não são necessários na classe de implementação específica:

public SomeReturnType optionalInterfaceMethodA(...) {
    throw new UnsupportedOperationException();
}

No Java 7 e anteriores, esse era realmente o único tipo de "método opcional" que existia, ou seja, um método que, se não implementado, gerava uma exceção UnsupportedOperationException. Esse comportamento é necessariamente especificado pelo contrato de interface (por exemplo, os métodos de interface opcionais do Java Collections Framework).

2. Um método padrão cuja reimplementação é opcional

Java 8 introduziu o conceito de métodos padrão . Esses são métodos cuja implementação pode ser e é fornecida pela própria definição de interface. Geralmente, só é possível fornecer métodos padrão quando o corpo do método pode ser escrito usando outros métodos de interface (ou seja, os "primitivos") e quando thispode significar "este objeto cuja classe implementou esta interface".

Um método padrão deve cumprir o contrato da interface (assim como qualquer implementação de outro método de interface deve). Portanto, especificar uma implementação do método de interface em uma classe de implementação fica a critério do autor (desde que o comportamento seja adequado ao seu propósito).

Nesse novo ambiente, o Java Collections Framework pode ser reescrito como:

public interface List<E> {
    :
    :
    default public boolean add(E element) {
        throw new UnsupportedOperationException();
    }
    :
    :
}

Dessa forma, o método "opcional" add()tem o comportamento padrão de lançar uma UnsupportedOperationException se a classe de implementação não fornecer um novo comportamento próprio, que é exatamente o que você gostaria que acontecesse e que é compatível com o contrato de List. Se um autor está escrevendo uma classe que não permite que novos elementos sejam adicionados a uma implementação de List, a implementação de add()é opcional porque o comportamento padrão é exatamente o que é necessário.

Nesse caso, a "terceira instrução" acima ainda é válida, porque o método foi implementado na própria interface.

3. Um método que retorna um Optionalresultado

O novo tipo final de método opcional é simplesmente um método que retorna um Optional. A Optionalclasse oferece uma maneira decididamente mais orientada a objetos de lidar com os nullresultados.

Em um estilo de programação fluente, como o tipo comumente visto ao codificar com a nova API Java Streams, um resultado nulo em qualquer ponto faz com que o programa trave com uma NullPointerException. A Optionalclasse fornece um mecanismo para retornar resultados nulos para o código do cliente de uma forma que permite o estilo fluente sem causar o travamento do código do cliente.

scottb
fonte
4

Se percorrermos o código de AbstractCollection.java em grepCode, que é uma classe ancestral para todas as implementações de coleção, isso nos ajudará a entender o significado dos métodos opcionais. Aqui está o código para o método add (e) na classe AbstractCollection. método add (e) é opcional de acordo com a interface de coleção

public boolean  add(E e) {

        throw new UnsupportedOperationException();
    } 

Método opcional significa que já está implementado em classes ancestrais e lança UnsupportedOperationException na chamada. Se quisermos tornar nossa coleção modificável, devemos substituir os métodos opcionais na interface da coleção.

IsAs
fonte
4

Bem, este tópico foi abordado para ... sim ... mas pense, uma resposta está faltando. Estou falando sobre os "métodos padrão" de interfaces. Por exemplo, vamos imaginar que você terá uma classe para fechar qualquer coisa (como um destruidor ou algo assim). Digamos que deva ter 3 métodos. Vamos chamá-los de "doFirst ()", "doLast ()" e "onClose ()".

Portanto, dizemos que queremos que qualquer objeto desse tipo pelo menos realize "onClose ()", mas os outros são opcionais.

Você pode perceber isso, usando os "Métodos Padrão" das interfaces. Eu sei, isso na maioria das vezes negaria o motivo de uma interface, mas se você estiver projetando uma estrutura, isso pode ser útil.

Então, se você quiser perceber dessa forma, seria o seguinte

public interface Closer {
    default void doFirst() {
        System.out.print("first ... ");
    }
    void onClose();
    default void doLast() {
        System.out.println("and finally!");
    }
}

O que aconteceria agora, se você, por exemplo, implementasse em uma classe chamada "Teste", o compilador estaria perfeitamente bem com o seguinte:

public class TestCloser implements Closer {
    @Override
    public void onClose() {
        System.out.print("closing ... ");
    }
}

com a saída:

first ... closing ... and finally!

ou

public class TestCloser implements Closer {
    @Override
    public void onClose() {
        System.out.print("closing ... ");
    }

    @Override
    public void doLast() {
        System.out.println("done!");
    }
}

com a saída:

first ... closing ... done!

Todas as combinações são possíveis. Qualquer coisa com "padrão" pode ser implementada, mas não deve, no entanto, qualquer coisa sem deve ser implementada.

Espero que não esteja totalmente errado o que eu respondo agora.

Tenham todos um ótimo dia!

[edit1]: Observação: isso só funciona no Java 8.

Thorben Kuck
fonte
Sim, desculpe, esqueci de mencionar isso ... Deve ser editado agora.
Thorben Kuck
1

Eu estava procurando uma maneira de implementar a interface de retorno de chamada, então a implementação de métodos opcionais era necessária, pois eu não queria implementar todos os métodos para cada retorno de chamada.

Então, em vez de usar uma interface, usei uma classe com implementação vazia, como:

public class MyCallBack{
    public void didResponseCameBack(String response){}
}

E você pode definir a variável de membro CallBack assim,

c.setCallBack(new MyCallBack() {
    public void didResponseCameBack(String response) {
        //your implementation here
    }
});

em seguida, chame-o assim.

if(mMyCallBack != null) {
    mMyCallBack.didResponseCameBack(response);
}

Dessa forma, você não precisa se preocupar em implementar todos os métodos por retorno de chamada, mas apenas sobrescrever aqueles de que você precisa.

green0range
fonte
0

Embora não responda à pergunta do OP, é importante notar que, a partir do Java 8, adicionar métodos padrão às interfaces é de fato factível . A defaultpalavra-chave colocada na assinatura do método de uma interface resultará em uma classe com a opção de sobrescrever o método, mas não exigirá isso.

Troy Stopera
fonte
0

Tutorial de coleções Java da Oracle:

Para manter o número de interfaces de coleção principais gerenciáveis, a plataforma Java não fornece interfaces separadas para cada variante de cada tipo de coleção. (Essas variantes podem incluir imutável, de tamanho fixo e apenas acréscimo.) Em vez disso, as operações de modificação em cada interface são designadas como opcionais - uma determinada implementação pode optar por não suportar todas as operações. Se uma operação não suportada for chamada, uma coleção lançará uma UnsupportedOperationException . As implementações são responsáveis ​​por documentar quais das operações opcionais elas suportam. Todas as implementações de uso geral da plataforma Java suportam todas as operações opcionais.

Trent Steele
fonte