Tipo inferido no se ajusta a la igualdad de error restricción para las variables no relacionadas

Adrian:

Tengo el siguiente fragmento de código

public class TeeingCollector {

    public static void main(String[] args) {
        //        var strs = List.of("abc");
        var dividedStrings = Stream.of("foo", "hello", "bar", "world")
            .collect(Collectors.teeing(
                        Collectors.filtering((String s) -> s.length() <= 3, Collectors.toList()),
                        Collectors.filtering((String s) -> s.length() > 3, Collectors.toList()),
                        List::of
                        ));
        System.out.println(dividedStrings);
    }
    private static class Employee {
        boolean isActive;

        public Employee(boolean isActive) {
            this.isActive = isActive;
        }

        public boolean isActive() {
            return isActive;
        }

        @Override
            public String toString() {
                return "Employee{" +
                    "isActive=" + isActive +
                    '}';
            }
    }
    private static class MaxMin {
        int max;
        int min;

        MaxMin(int max, int min) {
            this.max = max;
            this.min = min;
        }

        @Override
            public String toString() {
                return "MaxMin{" +
                    "max=" + max +
                    ", min=" + min +
                    '}';
            }
    }
}

Si ejecuto esa clase de terminal con java src/TeeingCollector.java me sale el siguiente error:

src/TeeingCollector.java:14: error: incompatible types: inferred type does not conform to equality constraint(s)
            .collect(Collectors.teeing(
                    ^
inferred: List<String>
equality constraints(s): List<Object>,R
where R,T,A are type-variables:
R extends Object declared in method <T,A,R>filtering(Predicate<? super T>,Collector<? super T,A,R>)
T extends Object declared in method <T,A,R>filtering(Predicate<? super T>,Collector<? super T,A,R>)
A extends Object declared in method <T,A,R>filtering(Predicate<? super T>,Collector<? super T,A,R>)
1 error
error: compilation failed

Si yo elimine la línea var strs = List.of("abc");a continuación, el código se ejecuta sin ningún problema.

versión de Java (para MacOS):

OpenJDK Runtime Environment (build 12+33)
OpenJDK 64-Bit Server VM (build 12+33, mixed mode, sharing)

ejecutar el mismo código con la versión siguiente (mayores) produce ningún error

OpenJDK Runtime Environment (build 12-ea+23)
OpenJDK 64-Bit Server VM (build 12-ea+23, mixed mode, sharing)

Nota: si compilo se ejecute, no tiene ningún errores tanto con construcciones para MacOS, por lo que parece que sólo java TeeingCollector.javano funciona correctamente

Holger:

TL; DR esto es obviamente un error, ya que el comportamiento del compilador depende de cosas sin relación alguna, incluidos los aspectos ambientales fuera del lenguaje Java.

Que simplifica su ejemplo y integré implementaciones de Collectors.teeingy Predicate.noten el ejemplo, para poder probar el código con las versiones de Java JDK de 9 a 12 JDK.

Tenga en cuenta que mientras que lo primero que pensé que se trataba de algún tipo de interacción de var's inferencia de tipos con la construcción genérica en la mano del lado derecho, más pruebas revelaron que el problema aún existe cuando se utilizan tipos explícitos para las variables, lo que permitió incluir JDK 9 en las pruebas.

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

import java.util.stream.Collector;

public class Temp5 {

  public static void main(String[] args) {
    // List<Character> strs = List.of("abc");
    List<List<Character>> lettersAndNumbers = Stream.of('5', 't', 'o', '9', 'p', '1', 'h')
        .collect(teeing(
                Collectors.filtering(Character::isLetter, Collectors.toList()),
                Collectors.filtering(not(Character::isLetter), Collectors.toList()),
                List::of    
        ));
    }

    public static <T, R1, R2, R>
    Collector<T, ?, R> teeing(Collector<? super T, ?, R1> downstream1,
                              Collector<? super T, ?, R2> downstream2,
                              BiFunction<? super R1, ? super R2, R> merger) {
        return teeing0(downstream1, downstream2, merger);
    }

    private static <T, A1, A2, R1, R2, R>
    Collector<T, ?, R> teeing0(Collector<? super T, A1, R1> downstream1,
                               Collector<? super T, A2, R2> downstream2,
                               BiFunction<? super R1, ? super R2, R> merger) {
        Objects.requireNonNull(downstream1, "downstream1");
        Objects.requireNonNull(downstream2, "downstream2");
        Objects.requireNonNull(merger, "merger");

        Supplier<A1> c1Supplier = Objects.requireNonNull(downstream1.supplier(), "downstream1 supplier");
        Supplier<A2> c2Supplier = Objects.requireNonNull(downstream2.supplier(), "downstream2 supplier");
        BiConsumer<A1, ? super T> c1Accumulator =
                Objects.requireNonNull(downstream1.accumulator(), "downstream1 accumulator");
        BiConsumer<A2, ? super T> c2Accumulator =
                Objects.requireNonNull(downstream2.accumulator(), "downstream2 accumulator");
        BinaryOperator<A1> c1Combiner = Objects.requireNonNull(downstream1.combiner(), "downstream1 combiner");
        BinaryOperator<A2> c2Combiner = Objects.requireNonNull(downstream2.combiner(), "downstream2 combiner");
        Function<A1, R1> c1Finisher = Objects.requireNonNull(downstream1.finisher(), "downstream1 finisher");
        Function<A2, R2> c2Finisher = Objects.requireNonNull(downstream2.finisher(), "downstream2 finisher");

        Collector.Characteristics[] characteristics;
        Set<Collector.Characteristics> c1Characteristics = downstream1.characteristics();
        Set<Collector.Characteristics> c2Characteristics = downstream2.characteristics();
        EnumSet<Collector.Characteristics> c = EnumSet.noneOf(Collector.Characteristics.class);
        c.addAll(c1Characteristics);
        c.retainAll(c2Characteristics);
        c.remove(Collector.Characteristics.IDENTITY_FINISH);
        characteristics = c.toArray(new Collector.Characteristics[0]);

        class PairBox {
            A1 left = c1Supplier.get();
            A2 right = c2Supplier.get();
            void add(T t) {
                c1Accumulator.accept(left, t);
                c2Accumulator.accept(right, t);
            }
            PairBox combine(PairBox other) {
                left = c1Combiner.apply(left, other.left);
                right = c2Combiner.apply(right, other.right);
                return this;
            }
            R get() {
                R1 r1 = c1Finisher.apply(left);
                R2 r2 = c2Finisher.apply(right);
                return merger.apply(r1, r2);
            }
        }
        return Collector.of(PairBox::new, PairBox::add, PairBox::combine, PairBox::get, characteristics);
    }
    @SuppressWarnings("unchecked")
    static <T> Predicate<T> not(Predicate<? super T> target) {
        Objects.requireNonNull(target);
        return (Predicate<T>)target.negate();
    }
}

Los resultados son desastrosos. Cada versión se puede tener una opinión diferente acerca de la corrección del código, incluso cuando acaba de cambiar la versión de menor importancia. Bueno, incluso el uso de OpenJDK en lugar de la distribución de Oracle puede tener un resultado diferente. Además, incluso los más mínimos cambios en el código pueden afectarlo. Como se ha señalado, cambiar el comentario en una declaración real puede cambiar el resultado, pero incluso la eliminación de la observación cambia el resultado para algunas versiones de JDK. Simplemente cambiar los valores , como el uso Stream.of('5', 't', 'o', '9', 'p', '1')en lugar de Stream.of('5', 't', 'o', '9', 'p', '1', 'h')cambios en el resultado para algunas versiones.

Mi conclusión es que algo en la implementación del compilador depende de algo estable para una configuración particular, pero en realidad impredecible, como el orden de iteración de un HashMap. Y la versión de JDK en sí parece ser parte de ella. Esto también explicaría por qué el resultado puede cambiar cuando se utiliza en lugar de MacOS Linux o Windows. Incluso la compilación de este archivo de origen junto con otro, archivo de origen no relacionada puede cambiar el resultado.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=137745&siteId=1
Recomendado
Clasificación