Inferência de tipo em Java 8


30

A introdução da nova notação lambda (consulte, por exemplo, este artigo ) no Java 8 exigirá algum tipo de inferência de tipo?

Se sim, como o novo sistema de tipos afetará a linguagem Java como um todo?

Respostas:


47

Há uma quantidade razoável de informações incorretas na resposta do catraca e no seu tópico de comentários. Vou responder aqui em uma resposta, já que um comentário é muito pequeno. Além disso, como essa é uma resposta, tentarei responder à pergunta original também. (Observe, no entanto, que eu não sou especialista em sistemas de tipos.)

Primeiro, as respostas curtas para a pergunta original são Sim e Não. Sim, o Java 8 terá consideravelmente mais inferência de tipo que o Java 7 e Não, não há um sistema de "novo" tipo no Java 8, embora haja algumas pequenas alterações .

O Java 8 ainda será digitado estaticamente e ainda terá a dicotomia entre classes e interfaces. Não há novos tipos, como tipos de função. O tipo de um lambda é essencialmente uma "interface funcional", que é uma interface comum com um único método abstrato.

As interfaces agora podem ter código na forma de métodos padrão, mas o modelo de herança única de classes e herança múltipla de interfaces permanece o mesmo. Existem alguns ajustes, é claro, como regras para resolução de métodos na presença de métodos padrão, mas os fundamentos são inalterados.

Qualquer tipo inferido por inferência de tipo pode ser gravado explicitamente. Para usar o exemplo de catraca ,

Collections.<MyClass>sort(list, (a, b) -> { return a.order - b.order; });

é basicamente açúcar para

Collections.<MyClass>sort(list,
    (Comparator<MyClass>)((MyClass a, MyClass b) -> { return a.order - b.order; }));

Portanto , a afirmação de sparkleshy "inferência de tipo não requer nenhuma extensão do sistema de tipos" está basicamente correta.

Mas, voltando ao açúcar sintático, repetirei minha afirmação de que uma expressão lambda não é açúcar sintático para uma classe interna anônima. Ratchet freak afirmou que uma expressão lambda é traduzida em uma instanciação anônima da classe interna, e Sparkleshy simplesmente reafirmou que um lambda é um açúcar sintático para uma classe interna anônima, mas essas declarações estão incorretas. Eles provavelmente são baseados em informações desatualizadas. As primeiras implementações de lambda implementaram lambdas dessa maneira, mas as coisas mudaram.

As expressões lambda são semanticamente diferentes das classes internas e são implementadas de maneira diferente das classes internas.

As expressões lambda são semanticamente diferentes das classes internas de duas maneiras. A avaliação de uma expressão lambda não precisa criar uma nova instância a cada vez. Eles também têm semântica de captura diferente, por exemplo, eles capturam isso de maneira diferente. Em uma classe interna, essa é a instância da classe interna, enquanto que em um lambda, essa é a instância anexa. Considere o seguinte:

public class CaptureThis {
    void a(Runnable r) { r.run(); }

    void b() {
        a(new Runnable() { public void run() { System.out.println(this); }});
        a(() -> System.out.println(this));
    }

    public String toString() { return "outer"; }

    public static void main(String[] args) { new CaptureThis().b(); }
}

Em uma compilação lambda JDK 8 recente (usei b69 ), a saída será algo como o seguinte:

CaptureThis$1@113de03
outer

Além disso, as expressões lambda são implementadas de maneira completamente diferente das classes internas. Se você comparar a saída desmontada, verá que o código da classe interna é compilado diretamente para a criação e chama um construtor de CaptureThis $ 1, enquanto a expressão lambda é compilada para uma instrução dinâmica invocada que obtém um Runnable por meios não especificados. Para obter uma explicação completa de como isso funciona e por que, consulte JavaOne 2012 de Brian Goetz, converse sobre Lambda: A Peek Under The Hood .


2
"eles capturam isso de maneira diferente. Em uma classe interna, esta é a instância da classe interna, enquanto que em um lambda, esta é a instância anexa. Considere o seguinte:" ainda pode ser feito com açúcar sintático, substituindo tudo thisporMyClass.this
catraca

4
Tudo o que vai além do assembler (e às vezes até isso) é sem dúvida um açúcar sintático. Mas lambdas não são açúcar sintático para classes internas (mais). Eles servem a um objetivo semelhante e têm algumas semelhanças, mas sob o capô são (notavelmente) diferentes.
Joachim Sauer

11
"As expressões Lambda são semanticamente diferentes das classes internas e são implementadas de forma diferente das classes internas.": Obrigado pela explicação muito boa (+1). O que ainda não está claro para mim é por que as lambdas não foram implementadas como açúcar sintático para classes especiais anônimas ou, em outras palavras, qual é a vantagem da complexidade extra introduzida pela nova semântica? Que problema isso resolve que não pode ser resolvido com classes internas anônimas? (Mas eu ainda tenho que olhar para o link que você postou, talvez eu vou encontrar a resposta lá.)
Giorgio

11
@Joachim Sauer: Eu consideraria o açúcar sintático uma nova sintaxe para uma semântica existente. Quando uma nova semântica é introduzida, acho que você não pode falar sobre açúcar sintático. Nesse sentido, não acho que você possa argumentar que tudo além do assembler é açúcar sintático.
Giorgio

3
@Giorgio: por que lambda não é apenas açúcar sintático para classes internas anônimas, acontece que houve uma grande discussão sobre isso. Este e-mail de Brian Goetz resume a decisão: mail.openjdk.java.net/pipermail/lambda-dev/2011-August/… . TL; DR deixa a porta aberta para a evolução futura e obtemos melhor desempenho hoje. Goetz está realmente respondendo a uma pergunta relacionada: Os objetos lambdas são? Mas se a resposta for Não ou talvez Talvez isso implique, eles não podem ser açúcar para as classes internas.
Stuart Marks
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.