Lambda Ausdrücke: @FunctionalInterface Instanzen auf null prüfen

Sasa

Mitglied
Hallo,

kann ich in Java 8 prüfen, ob eine @FunctionalInterface Instanz von einem null Object kommt?

Das Problem ist, wenn ich so eine Instanz habe, tritt eine java.lang.NullPointerException auf, wenn ich deren Funktion aufrufe:
Java:
@Test
public void testNullObjectMethod() {
    Collection<?> collection = null;
    Predicate<Collection<?>> predicate = collection::equals;
    Assert.assertNotNull(predicate); // OK
    Assert.assertTrue(predicate instanceof Predicate<?>); // OK
    predicate.test(Collections.EMPTY_LIST); // NPE
}
 
Ich bin da noch selbst ein Neuling ... sofern ich das richtig Verstanden habe, muss man in diesen Fällen die Klasse "Optional" nutzen.
 
Ich bin da noch selbst ein Neuling ... sofern ich das richtig Verstanden habe, muss man in diesen Fällen die Klasse "Optional" nutzen.

Was er versucht ist einfach eine "Methodenreferenz" von einem Objekt zu erhalten das nicht existiert. Die NPE fliegt nicht dort wo er sie markiert hat, sondern schon in der Zeile in der er versucht sich die Methodenreferenz von einer Null-Referenz zu organisieren. Auch Optional wird da nicht helfen, stopfst du da null rein und versuchst den Wert zu holen (die Collection) fliegt genauso eine Exception aber dieses mal wirft Optional die.
 
Was er versucht ist einfach eine "Methodenreferenz" von einem Objekt zu erhalten das nicht existiert. Die NPE fliegt nicht dort wo er sie markiert hat, sondern schon in der Zeile in der er versucht sich die Methodenreferenz von einer Null-Referenz zu organisieren. Auch Optional wird da nicht helfen, stopfst du da null rein und versuchst den Wert zu holen (die Collection) fliegt genauso eine Exception aber dieses mal wirft Optional die.
Meine ursprüngliche Erwartung war auch, dass ich die Exception in Zeile 4. bekomme (Für das Beispiel zeigt Eclipse dort auch eine Warnung an "Null pointer access: The variable collection can only be null at this location").

Aber die Ausführung geht weiter - die Assertions in Zeile 5. und 6. werden erfolgreich durchlaufen! Die Exception fliegt tatsächlich erst in Zeile 7.
 
[...]Die Exception fliegt tatsächlich erst in Zeile 7.

Ok, lass es mich so formulieren. Es gibt Eclipse und es gibt die Realität. Eclipse verwendet standardmäßig seinen eigenen Compiler um *.class Dateien zu erzeugen. Deren Bytecode muss nicht mit dem übereinstimmen was der javac ausspuckt der bei einem JDK mit dabei ist. Und genau auf dieses Problem bist du gestoßen.

Wird der folgende Code über die Kommandozeile mit den JDK-Werkzeugen kompiliert.
Java:
import java.util.Collection;
import java.util.Collections;
import java.util.function.Predicate;

import org.junit.Assert;
import org.junit.Test;

public class methodrefTest {
    @Test
    public void myMethod(){
    	Collection<?> collection = null;
        Predicate<Collection<?>> predicate = collection::equals;
        Assert.assertNotNull(predicate); // OK
        Assert.assertTrue(predicate instanceof Predicate<?>); // OK
        predicate.test(Collections.EMPTY_LIST); // NPE
    }
}

Dann erhält man bei Ausführung über die Kommandozeile folgendes Ergebnis, das deutlich zeigt das die Exception genau dort fliegt wo man sie auch erwarten würde.

java.lang.NullPointerException
at methodrefTest.myMethod(methodrefTest.java:12)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runners.Suite.runChild(Suite.java:127)
at org.junit.runners.Suite.runChild(Suite.java:26)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at org.junit.runner.JUnitCore.run(JUnitCore.java:138)
at org.junit.runner.JUnitCore.run(JUnitCore.java:117)
at org.junit.runner.JUnitCore.runMain(JUnitCore.java:96)
at org.junit.runner.JUnitCore.runMainAndExit(JUnitCore.java:47)
at org.junit.runner.JUnitCore.main(JUnitCore.java:40)

Ich bemerkte das nicht sofort, weil ich IntelliJ nutze. IntelliJ verwendet standardmäßig den javac des eingestellten JDK.
 
Zuletzt bearbeitet:
@nvidia: OK, du hast recht. Das war ein Problem von Eclipse - übrigens Version="Luna M7 Release (4.4.0M7)" (Ich hoffe, dass sie das bis zum finalen Release beheben...)
 
@nvidia: OK, du hast recht. Das war ein Problem von Eclipse - übrigens Version="Luna M7 Release (4.4.0M7)" (Ich hoffe, dass sie das bis zum finalen Release beheben...)
Macht es Eclipse aber nicht eigentlich richtig, da
Code:
collection::equals
doch eine statische Methoden-Referenz ist, also identisch mit [c]Collection::equals[/c]?

Zum nachdenken (aus einem anderen Forum):
Java:
public class FluentCallTest
{
    public static void main(String[] args)
    {
        das().funktioniert().nicht();
    }    
    static FluentCallTest das()
    {
        System.err.print("das ");
        return null;
    }
    static FluentCallTest funktioniert()
    {
        System.err.print("funktioniert ");
        return null;
    }
    FluentCallTest nicht()
    {
        System.err.println("nicht");
        return null;
    }
}
Ergibt "das funktioniert" und dann eine NPE. Weil funktioniert() static ist, ist der Rückgabewert von das() egal.
 
Zuletzt bearbeitet:
Macht es Eclipse aber nicht eigentlich richtig, da
Code:
collection::equals
doch eine statische Methoden-Referenz ist, also identisch mit [c]Collection::equals[/c]?

Nein, in dem Kontext ist es keine statische Methodenreferenz. Es wird versucht eine Methodenreferenz von einem "Objekt" (collection) zu holen welches zufälligerweise null ist. Und zu deinem Beispiel, das war doch schon immer so und hat auch nichts mit den neuen Lambdaausdrücken zu tun.
 
Zuletzt bearbeitet:

Zurück
Oben