Estou projetando uma interface com dois métodos relacionados, semelhantes a este:
public interface ThingComputer {
default Thing computeFirstThing() {
return computeAllThings().get(0);
}
default List<Thing> computeAllThings() {
return ImmutableList.of(computeFirstThing());
}
}
Cerca de metade das implementações computará apenas uma coisa, enquanto a outra metade poderá computar mais.
Isso tem algum precedente no código Java 8 amplamente utilizado? Eu sei que Haskell faz coisas semelhantes em algumas classes ( Eq
por exemplo).
O lado positivo é que tenho que escrever um código significativamente menor do que se eu tivesse duas classes abstratas ( SingleThingComputer
e MultipleThingComputer
).
A desvantagem é que uma implementação vazia é compilada, mas explode em tempo de execução com a StackOverflowError
. É possível detectar a recursão mútua com ae ThreadLocal
fornecer um erro melhor, mas isso adiciona uma sobrecarga ao código que não é de buggy.
fonte
throw new Error();
ou algo estúpido, apenas que a própria interface não deve ter um contrato frágil por meio dedefault
métodos.abstract
existe para forçá- los a resolvê-lo.Respostas:
TL; DR: não faça isso.
O que você mostra aqui é um código quebradiço.
Uma interface é um contrato. Ele diz "independentemente de qual objeto você obtém, ele pode executar X e Y". Como está escrito, sua interface faz nem X nem Y, pois é garantido para causar um estouro de pilha.
Lançar um erro ou subclasse indica um erro grave que não deve ser capturado:
Além disso, VirtualMachineError , a classe pai de StackOverflowError , diz o seguinte:
Seu programa não deve se preocupar com os recursos da JVM . Esse é o trabalho da JVM. Fazer um programa que causa um erro da JVM como parte da operação normal é ruim. Ele garante que seu programa falhará ou obriga os usuários dessa interface a capturar erros com os quais não deve se preocupar.
Talvez você conheça Eric Lippert por empreendimentos como "membro emérito do comitê de design de linguagem C #". Ele fala sobre os recursos da linguagem que levam as pessoas ao sucesso ou ao fracasso: embora esse não seja um recurso da linguagem ou faça parte do design da linguagem, seu argumento é igualmente válido quando se trata de implementar interfaces ou usar objetos.
Fonte: C ++ e o poço do desespero
Ter uma interface ativada
StackOverflowError
por padrão leva os desenvolvedores ao Pit of Despair e é má idéia . Em vez disso, empurre os desenvolvedores em direção ao poço do sucesso . Torná-lo fácil de usar sua interface corretamente e sem bater a JVM.Delegar entre os métodos é bom aqui. No entanto, a dependência deve seguir um caminho. Eu gosto de pensar na delegação de método como um gráfico direcionado . Cada método é um nó no gráfico. Cada vez que um método chama outro método, desenhe uma borda do método de chamada para o método chamado.
Se você desenhar um gráfico e perceber que é cíclico, é um cheiro de código. Esse é um potencial para empurrar os desenvolvedores para o poço do desespero. Observe que não deve ser categoricamente proibido, apenas que é preciso ter cuidado . Algoritmos recursivos especificamente terão ciclos no gráfico de chamada: tudo bem. Documente e avise os desenvolvedores. Se não for recursivo, tente interromper esse ciclo. Se não puder, descubra quais entradas podem causar um estouro de pilha e atenue-as ou documente-a como último caso, se nada mais funcionar.
fonte