Os methods padrão do Java 8 quebram a compatibilidade de fonts?

Tem sido geralmente o caso que o código-fonte Java foi compatível para frente. Até o Java 8, até onde eu sei, as classs compiladas e a origem foram compatíveis com versões posteriores do JDK / JVM. [Atualização: isso não está correto, veja comentários re ‘enum’, etc, abaixo.] No entanto, com a adição de methods padrão no Java 8, este parece não ser mais o caso.

Por exemplo, uma biblioteca que eu tenho usado tem uma implementação de java.util.List que inclui uma List sort() . Este método retorna uma cópia do conteúdo da lista classificada. Esta biblioteca, implementada como uma dependência de arquivo jar, funcionou bem em um projeto que está sendo construído usando o JDK 1.8.

No entanto, mais tarde tive a oportunidade de recompilar a própria biblioteca usando o JDK 1.8 e descobri que a biblioteca não mais compila: a class List -implementing com seu próprio método sort() agora entra em conflito com o Java 8 java.util.List.sort() método padrão. O método padrão do Java 8 sort() classifica a lista no local (retorna void ); o método sort() da minha biblioteca – já que retorna uma nova lista classificada – possui uma assinatura incompatível.

Então minha pergunta básica é:

  • O JDK 1.8 não introduz uma incompatibilidade de encaminhamento para código-fonte Java devido a methods padrão?

Além disso:

  • Esta é a primeira dessas mudanças incompatíveis com a frente?
  • Isso foi considerado ou discutido quando os methods padrão foram projetados e implementados? Está documentado em algum lugar?
  • O inconveniente (reconhecidamente pequeno) foi descontado em relação aos benefícios?

Segue-se um exemplo de código que compila e executa sob 1.7 e é executado em 1.8 – mas não compila em 1.8:

 import java.util.*; public final class Sort8 { public static void main(String[] args) { SortableList l = new SortableList(Arrays.asList(args)); System.out.println("unsorted: "+l); SortableList s = l.sort(Collections.reverseOrder()); System.out.println("sorted : "+s); } public static class SortableList extends ArrayList { public SortableList() { super(); } public SortableList(Collection col) { super(col); } public SortableList sort(Comparator cmp) { SortableList l = new SortableList(); l.addAll(this); Collections.sort(l, cmp); return l; } } } 

O seguinte mostra este código sendo compilado (ou falhando) e sendo executado.

 > c:\tools\jdk1.7.0_10\bin\javac Sort8.java > c:\tools\jdk1.7.0_10\bin\java Sort8 this is a test unsorted: [this, is, a, test] sorted : [this, test, is, a] > c:\tools\jdk1.8.0_05\bin\java Sort8 this is a test unsorted: [this, is, a, test] sorted : [this, test, is, a] > del Sort8*.class > c:\tools\jdk1.8.0_05\bin\javac Sort8.java Sort8.java:46: error: sort(Comparator) in SortableList cannot implement sort(Comparator) in List public SortableList sort(Comparator cmp) { ^ return type SortableList is not compatible with void where V,E are type-variables: V extends Object declared in class SortableList E extends Object declared in interface List 1 error 

O JDK 1.8 não introduz uma incompatibilidade de encaminhamento para código-fonte Java devido a methods padrão?

Qualquer novo método em uma superclass ou interface pode quebrar a compatibilidade. Os methods padrão tornam menos provável que uma alteração em uma interface rompa a compatibilidade. No sentido de que os methods padrão abrem as portas para adicionar methods às interfaces, você poderia dizer que os methods padrão podem contribuir para alguma compatibilidade quebrada.

Esta é a primeira dessas mudanças incompatíveis com a frente?

Quase certamente não, já que subclassificamos classs da biblioteca padrão desde o Java 1.0.

Isso foi considerado ou discutido quando os methods padrão foram projetados e implementados? Está documentado em algum lugar?

Sim, foi considerado. Veja o artigo de Brian Goetz de agosto de 2010, “Evolução da interface através de methods de defensor público” :

  1. Compatibilidade de fonte

É possível que esse esquema possa introduzir incompatibilidades de origem na medida em que as interfaces de biblioteca sejam modificadas para inserir novos methods incompatíveis com methods em classs existentes. (Por exemplo, se uma class tiver um método xyz () flutuante e implementar Collection, e adicionarmos um método xyz () com valor de int a Collection, a class existente não será mais compilada.)

O inconveniente (reconhecidamente pequeno) foi descontado em relação aos benefícios?

Antes, mudar uma interface definitivamente iria quebrar a compatibilidade. Agora pode acontecer. Indo de ‘definitivamente’ para ‘poder’ pode ser visto positivamente ou negativamente. Por um lado, torna-se viável adicionar methods às interfaces. Por outro lado, isso abre a porta para o tipo de incompatibilidade que você viu, não apenas com classs, mas também com interfaces.

Os benefícios são maiores do que os inconvenientes, porém, como citado no topo do artigo de Goetz:

  1. Declaração do problema

Uma vez publicado, é impossível adicionar methods a uma interface sem interromper as implementações existentes. Quanto maior o tempo desde que uma biblioteca foi publicada, mais provável é que essa restrição cause sofrimento aos seus mantenedores.

A adição de closures para a linguagem Java no JDK 7 coloca uma ênfase adicional nas interfaces Collection antigas; Um dos benefícios mais significativos dos encerramentos é que ele permite o desenvolvimento de bibliotecas mais poderosas. Seria decepcionante adicionar um recurso de linguagem que permita melhores bibliotecas e, ao mesmo tempo, não estenda as bibliotecas principais para aproveitar esse recurso.

O JDK 1.8 não introduz uma incompatibilidade de encaminhamento para código-fonte Java devido a methods padrão?

Sim como você viu a si mesmo.

Esta é a primeira dessas mudanças incompatíveis com a frente?

Não. A palavra-chave enum Java 5 também estava quebrando, porque antes disso você poderia ter variables ​​nomeadas que não seriam mais compiladas no Java 5 +

Isso foi considerado ou discutido quando os methods padrão foram projetados e implementados? Está documentado em algum lugar?

Sim Orcale Java 8 source incompatibility description

O inconveniente (reconhecidamente pequeno) foi descontado em relação aos benefícios?

sim

Podemos desenhar um paralelo com a class abstrata. Uma class abstrata deve ser subclassificada para que os methods abstratos possam ser implementados. A class abstrata em si contém methods concretos que invocam os methods abstratos. A class abstrata está livre para evoluir, adicionando methods mais concretos; e esta prática pode quebrar subclasss.

Portanto, o problema exato que você descreveu existia antes mesmo do Java8. O problema é muito mais manifestado nas APIs de coleta porque há muitas subclasss em estado selvagem.

Embora a principal motivação do método padrão fosse adicionar alguns methods úteis às APIs de collections existentes sem quebrar as subclasss, eles tiveram que exercer um grande autocontrole de fazê-lo demais, por medo de quebrar as subclasss. Um método padrão é adicionado somente se for absolutamente necessário. A verdadeira questão aqui é, por que List.sort é considerado absolutamente necessário. Eu acho que isso é discutível.

Independentemente do motivo pelo qual o método padrão foi introduzido em primeiro lugar, ele é agora uma ótima ferramenta para projetistas de APIs, e devemos tratá-lo da mesma forma que methods concretos em classs abstratas – eles precisam ser projetados com cuidado antecipadamente; e novos devem ser introduzidos com grande caucanvas.

Ironicamente, os methods padrão nas interfaces foram introduzidos para permitir que bibliotecas existentes usando essas interfaces não quebrem, enquanto introduzem novas funcionalidades massivas nas interfaces. (compatibilidade com versões anteriores.)

Conflitos como esse método de sort podem surgir. Algo para pagar pela funcionalidade extra. No seu caso, também algo para investigar (novas funcionalidades devem ser usadas em vez disso?).

As quebras de compatibilidade para frente com Java são pequenas, mais em seu sistema de digitação, que foi constantemente ampliado. Primeiro com tipos genéricos e agora com tipos inferidos de interfaces funcionais. De versão para versão e de compilador para compilador houve pequenas diferenças.

Lendo este assunto, eu estava pensando em sua solução.
Os methods padrão solucionaram os problemas de compatibilidade com versões anteriores, mas problemas de compatibilidade futuros existirão.
Eu acho que em vez de estender as classs existentes, em tais casos, podemos ter nossas interfaces específicas de aplicativo para adicionar algum comportamento desejado à nossa class. Podemos implementar essa interface específica de aplicativo e usá-la.