Why do I need a functional Interface to work with lambdas?

36,549

Solution 1

When you write :

TestInterface i = () -> System.out.println("Hans");

You give an implementation to the void hans() method of the TestInterface.

If you could assign a lambda expression to an interface having more than one abstract method (i.e. a non functional interface), the lambda expression could only implement one of the methods, leaving the other methods unimplemented.

You can't solve it by assigning two lambda expressions having different signatures to the same variable (Just like you can't assign references of two objects to a single variable and expect that variable to refer to both objects at once).

Solution 2

The most important reason why they must contain only one method, is that confusion is easily possible otherwise. If multiple methods were allowed in the interface, which method should a lambda pick if the argument lists are the same ?

interface TestInterface {
    void first();
    void second(); // this is only distinguished from first() by method name
    String third(); // maybe you could say in this instance "well the return type is different"
    Object fourth(); // but a String is an Object, too !
}

void test() {
    // which method are you implementing, first or second ?
    TestInterface a = () -> System.out.println("Ido mein ado mein");
    // which method are you implementing, third or fourth ?
    TestInterface b = () -> "Ido mein ado mein";
}

Solution 3

You seem to be looking for anonymous classes. The following code works:

public class Test {

    public static void main(String...args) {
        TestInterface i = new TestInterface() {
            public void hans() {
                System.out.println("Hans");
            }
            public void hans(String a) {
                System.out.println(a);
            }
        };

        i.hans();
        i.hans("Hello");
    }
}

public interface TestInterface {
    public void hans();
    public void hans(String a);
}

Lambda expressions are (mostly) a shorter way to write anonymous classes with only one method. (Likewise, anonymous classes are shorthand for inner classes that you only use in one place)

Solution 4

You do not have to create a functional interface in order to create lambda function. The interface allow you to create instance for future function invocation.

In your case you could use already existing interface Runable

Runnable r = () -> System.out.println("Hans");

and then call

r.run();

You can think of lambda -> as only short hand for:

Runnable r = new Runnable() {
     void run() {
          System.out.println("Hans");`
     }
}

With lambda you do not need the anonymous class, that is created under the hood in above example.

But this has some limitation, in order to figure out what method should be called interface used with lambdas must be SAM (Single Abstract Method). Then we have only one method.

For more detailed explanation read:

Introduction to Functional Interfaces – A Concept Recreated in Java 8

Solution 5

  • In java when an interface is implemented, all its abstract methods are needed to implemented (otherwise the implementing class will have to be an interface).

  • Java compiler expands lambda expression internally with a class definition with the method, and a statement to instantiate this class. Currently java doesn't support/provide a way for more than 1 lambda to be associated with 1 interface.

      public class Test {
          public static void main(String...args) {
              TestInterface i = () -> System.out.println("Hans"); // this will not compile as the implementation for public void hans(String a); can not be provided/is not found
              //i = (String a) -> System.out.println(a); //this can not add an implementation for 2nd method to i after compilation of 1st lambda
           }
      }
      public interface TestInterface {
          public void hans();
          public void hans(String a);
      }
    

This the reason lambda in java only works with an interface with one method or functional interface.

Share:
36,549

Related videos on Youtube

codepleb
Author by

codepleb

#frontend #pwa #angular #vuejs #nodejs #expressjs #playwright #cryptocurrency #blockchain #dag

Updated on January 21, 2021

Comments

  • codepleb
    codepleb over 3 years

    I think this question is already somewhere out there, but I wasn't able to find it.

    I don't understand, why it's necessary to have a functional interface to work with lambdas. Consider the following example:

    public class Test {
    
        public static void main(String...args) {
            TestInterface i = () -> System.out.println("Hans");
    //      i = (String a) -> System.out.println(a);
    
            i.hans();
    //      i.hans("Hello");
        }
    }
    
    public interface TestInterface {
        public void hans();
    //  public void hans(String a);
    }
    

    This works without problems, but if you uncomment the commented lines, it doesn't. Why? In my understanding, the compiler should be able to distinguish the two methods, since they have different input-parameters. Why do I need a functional interface and blow up my code?

    EDIT: The linked duplicates didn't answer my question because I'm asking about different method-parameters. But I got some really useful answers here, thanks to everyone who helped! :)

    EDIT2: Sorry, I'm obviously not a native speaker, but to precise myself:

    public interface TestInterface {
        public void hans();                 //has no input parameters</br>
        public void hans(String a);         //has 1 input parameter, type String</br>
        public void hans(String a, int b);  //has 2 input parameters, 1. type = String, 2. type = int</br>
        public void hans(int a, int b);     //has also 2 input parameters, but not the same and a different order than `hans(String a, int a);`, so you could distinguish both
    }
    
    public class Test {
    
        public static void main(String...args) {
            TestInterface i = () -> System.out.println("Hans");
            i = (String a) -> System.out.println(a);
            i = (String a, int b) -> System.out.println(a + b);
            i = (int a, int b) -> System.out.println(a);
    
            i.hans(2, 3);   //Which method would be called? Of course the one that would take 2 integer arguments. :)
        }
    }
    

    All I'm asking is about the arguments. The method name doesn't matter, but each method takes an unique order of different arguments and because of that, Oracle could have implemented this feature instead just making a single method possible per "Lambda-Interface".

    • user253751
      user253751 over 8 years
      int i = 7; i = 5; System.out.println(i); // wouldn't it be awesome if this would print both 7 and 5?
    • a better oliver
      a better oliver over 8 years
      I really don't understand your reasoning. If something doesn't implement an interface, then how can it be of that type? An iPod doesn't count as iPhone just because you don't happen to make phone calls.
    • codepleb
      codepleb over 8 years
      @immibis: What you describe here is different from what I asked. :)
    • user253751
      user253751 over 8 years
      @TrudleR You seemed to be asking: <some type> i = <some value>; i = <some other value>; // why can't i be both values at once here? The variable i can hold a reference to one object, no matter whether that's an object created by a lambda expression or an instance of a class you wrote explicitly.
    • codepleb
      codepleb over 8 years
      @immibis: Yeah, I know that. I wrote on another answer, that it's more about the idea and not about the implementation. But you do something completely different, that makes sense in absolutely no case, since you don't know which value you would need to print out. In my example, if it were possible, the compiler would know which method he would have to take.
    • user253751
      user253751 over 8 years
      @TrudleR So you want to do something like TestInterface i = new TestInterface() {void hans() {System.out.println("Hans");} void hans(int a) {System.out.println(a);} /* and so on */};?
    • Marc_L
      Marc_L over 3 years
      somehow this looks to me, as if Java wanted to make it cool and easy like with javascript arrow functions, then messed it up totally and blew up the code with unnecessary functional interfaces.. ;)
  • codepleb
    codepleb over 8 years
    Hm, sounds logical, but I still wonder why Oracle did it like this. If you could just call methods for which you implemented a body, it would somehow be more intuitive. This is of course a problem of the interface-concept and not of lambdas itself. Thank you. :)
  • Damian Leszczyński - Vash
    Damian Leszczyński - Vash over 8 years
    @TrudleR, then Java would be a functional language. If you are interested in such implementation on JVM. You could try out the Scala language.
  • codepleb
    codepleb over 8 years
    Hm, Scala Scala Scala. :D I think I really need to learn this language. Everyone is talking about it lastly. Is my case possible with that language? That would really be beautiful.
  • codepleb
    codepleb over 8 years
    Yeah I know that, it should just result in an Exception if you want to run such code. But I thought that different arguments should be clear for the compiler. If you declare 2 methods with the same name and parameters, it also gives an exception, so why should Oracle prevent it here?
  • Holger
    Holger over 8 years
    @TrudleR: “if you could just call methods for which you implemented a body”—how do you know which methods are implemented when you have an instance of an arbitrary interface implementation?
  • Pelit Mamani
    Pelit Mamani over 8 years
    TrudleR, you got me thinking here :) but IMHO they'd be opening a Pandora box if they allow to "just call methods for which you implemented a body"...: lambda defines 1 method,while your interface is a contract for 2; deviating from this contract would really confuse the receive of your object.. plus if your interface has several methods with a String parameter, the compiler won't know which one is matched by the lambda expression. If you want to go radical, you could consider node.js which simply doesn't bother with contracts(=interfaces)...
  • codepleb
    codepleb over 8 years
    @Holger + Pelit: Lambdas don't make anonymous classes obsolete. This would also count for Interfaces: As long as the parameters are different, you could put in multiple methods in a "Functional Interface". If you would have multiple methods with the same parameters, you could still do it the old way. But this is also problematic, since it adds complexity. I just somehow feel that the current solution would have a lot of potential for new features. :)
  • Holger
    Holger over 8 years
    @TrudleR: I don’t get your point. Surely, neither anonymous classes nor interfaces are obsolete, nobody said something different. That doesn’t justify your request of being able to implement a multi-method interface by defining one valid method and all others being broken. I ask again, how is a programmer (or compiler) seeing a reference of that interface type (which may hold an arbitrary implementation) supposed to know which method is the one that is not broken? I’m not talking about the place where you define your n-1 broken implementation but the place where the object is being used.
  • codepleb
    codepleb over 8 years
    @Holger: Ok, I see I am messing things a bit up by commenting on different answers. I think I explained myself wrong, so I start over: Yes, Multi-Method Interfaces, but multiple valid methods (like the ones in my "question"). But IF you have multiple methods with the same input-parameters (method name doesn't matter), you would need to use the old fashioned way. But I think, you could increase the possibilities of lambdas, if multi-method-interfaces would be possible to use with lambdas. An interface with multiple methods while only 1 is valid would be dumb of course, that's not what I meant.
  • codepleb
    codepleb over 8 years
    @Holger: To precise myself: You can have as much methods as you want in your interface, as long as the input-parameters of those methods are unique for each method. You could call a method like this (someString, someInt, someDouble) -> {sysout(someString, someInt, someDouble)}. The compiler would just look which method signature would fit to the parameters you call that lambda expression and use this. Sorry for my bad english. It can get very complicated if I try to explain something, since I'm obviously not a native speaker.
  • Holger
    Holger over 8 years
    In your question, you are assigning the variable i twice (if you uncomment the lines). Just to get your idea correctly… you declare a variable of type TestInterface and initialize it with a lambda expression which ought to mean something like “create an incomplete implementation of TestInterface with only one method”, then you assign the variable a different lambda expression which ought to mean “merge this lambda expression into the implementation class behind that reference, so that now both methods are defined”? Seriously?
  • codepleb
    codepleb over 8 years
    @Holger: I edited the main question. It doesn't have to be exactly like this, but some solution in this way. I know that this would add a lot of complexity, but you could also do a wrapper for it. Don't just look at my synthax, please also look at the idea. After all the answers I got it's clear to me that this would bring up more negative aspects than positive ones. :)
  • Holger
    Holger over 8 years
    The problem with your question is that you are emphasizing the fact that each method has a different signature all the time. This makes only sense if you stick with a “one lambda expression is associated with one of these methods” idea. If you are just thinking about a compact way of implementing interfaces, the main reason that it doesn’t work with lambda expressions is, that they are not supposed to be an interface implementation helper. →Lambda expressions are functions. The functional interfaces are just a vehicle to add them to Java without changing the type system, not their purpose.
  • codepleb
    codepleb over 8 years
    Hey I'm not that much of a newbie. :) Of course I know how I can bring it to work. I just wanted to ask, why lambdas are cut to only work with functional interfaces. It's clear that I would need to write anonymous classes (or implement a class itself) for my case. All I wanted to say is, that I think Oracle could have extended the use of lambdas. But after the discussions here it's clear why they force you use functional interfaces. But thanks anyways.
  • user253751
    user253751 over 7 years
    @TrudleR If they extended lambdas in the way you're thinking of, then they'd be the same as anonymous classes, so what would be the point?
  • greenhorn
    greenhorn over 6 years
    Good example. Thanks
  • Alexander Mills
    Alexander Mills over 5 years
    How about the first method defined in order? :) You could have 5 methods on an interface and when using an interface instance as a lambda param, the first method of 5 would be used.