Location>code7788 >text

JAVA Foundation 5-Functional Interface Implementation

Popularity:811 ℃/2024-09-19 11:50:11

I'm singling this out because I was personally blown away by a source code.

So, my aim is to take a look at this shocking implementation and mimic it, and eventually post the regular implementation as well, so that the reader can see the relatively complete implementation

Note: The code in this article is based on JDK17

 

A code that blows people away

()

 public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>(ArrayList::new, List::add,
                                   (left, right) -> { (right); return left; },
                                   CH_ID);
    }

Let's take a look at the constructor for CollectorImpl:

CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Set<Characteristics> characteristics) {
            this(supplier, accumulator, combiner, castingIdentity(), characteristics);
        }

The second parameter isBiConsumer<A, T>, and then look at the interface methods of BiConsumer.

void accept(T t, U u);

 

According to normal logic, the toList() call to CollectorImpl should pass a method with two parameters, but there is only one parameter.

There are 2 realizations:

boolean add(E e)
void add(int index, E element);

Obviously, it's not possible to point to that two-parameter implementation, since the parameter types obviously don't match, and instead it can only point to theboolean add(E e)

But the problem add(E e) looks even more unworthy because it has only one argument.

The reality is that the compiler does not report errors and gets the correct result.

Why?

Thinking about it, I can only sayJCP changed the rules - to get there, JCP went against the grain to allow unique implementations

In previous source code, it seems like all we've seen is a requirement for the number of parameters and type matching?

 

II. My parodies and possible interpretations

To confirm this unique implementation of a functional interface, I made a a test in the test code:

1. Create a class similar to ArrayList

2. Wrote a test code to verify the peculiar implementation

The specific code is as follows:


package ;

/**
* middle-school student
* @param name
* @param age
* @param gender
*/
public record MiddleStudent(
String name, Integer age, String gender
) {
}


package ;

import ;

/**
* Used to demonstrate the shockinglambda displayed formula*
* <br/>
* <br/> As a comparison, take a look at{@linkplain Several basic implementations of functional interfaces}
* @author lzfto
* @date 2024/09/12
*/
public class ShockingList {
private MiddleStudent[] room;
public ShockingList() {
this.room = new MiddleStudent[10];
}

public void add(MiddleStudent student) {
expand();
//findroomThe last one is not fornulllocation,Then addstudent
for (int i = 0; i < room.length - 1; i++) {
if (this.room[i] == null) {
this.room[i] = student;
return;
}
}
System.("Excess room capacity,Unable to insert new member");
}

private void expand(){
//in the event thatroomThe last one is notnullSo.roomexpansion10location
if (this.room[this.room.length-1] != null) {
MiddleStudent[] temp = new MiddleStudent[this.room.length + 10];

//particle marking the following noun as a direct objectroomare all copied to the elements oftempin, and thendirectionaltemp
for (int i = 0; i < this.room.length; i++) {
temp[i] = this.room[i];
}
this.room = temp;
}
}

public static void main(String[] args) {
/**
* Demonstrating this strangeBiConsumerThe usage, or rather the syntax of the Lang hit
*/
ShockingList list = new ShockingList();
(new MiddleStudent("John Doe", 18, "male"));
BiConsumer<ShockingList, MiddleStudent> consumer = ShockingList::add;
(list, new MiddleStudent("the fourth child in the family", 19, "male"));
for (MiddleStudent middleStudent : list.room) {
if(middleStudent != null){
System.(middleStudent);
}
}
}
}
 

After testing, the output is shown below:

 

Based on the java example and my own writing examples, I can only come up with this sort of guess:

If the functional interface method F requires 2 arguments (R ,T), then when referencing an object method (assuming the object is called Test and the method is shockMe) that implements the functional interface, it is permissible to reference such an interface:.

can have one parameter of type T. The return type of ShockMe's method is the same as the return type of F, or both.

Type R itself.

Then JCP considers it compliant.

Based on this speculation, then it may be possible to also allow the case: that F has n arguments, but ShockMe has n-1 arguments. It is not verified for the time being.

 

Why would JCP allow such implementations to be viable anymore? Presumably for backwards compatibility, not wanting to waste the various implementations that already exist.

Let's think about it the other way around: if this is not allowed, then what should JAVA do?

Take toList() for example, then you have to add an implementation method, or write a few extra utility classes. jcp didn't know what to think, and came up with this relatively other implementation.

While this realization has its benefits:Backwards compatible and not wasteful. But it also results in code that is not easy to read (yes, I was confused for a long time).

I don't know if other languages have a similar situation.

III. 5 implementations of the functional interface standard

The following code, which is also available in my other posts:JAVA Fundamentals No. 4 - Introduction to Lang typing expressions, functional interfaces, streams

Repeat for convenience

package ;

import ;
import ;
import ;
import ;

/**
 * This class demonstrates several implementations of the functional interface:.
 * </br>
 * </br> 1. Using an implementation class - the most traditional.
 * </br> 2. Use Lambda expressions - still more convenient!
 * </br> 3. Use anonymous classes - pretty much the same as Lang.
 * </br> 4. Method references - applying another homomorphic method (polytope to instance)
 * </br> 5. Constructor references - applying another isomorphic constructor method.
 * </br> 6. Static method references - apply another isomorphic static method.
 Static method references - apply another isomorphic static method * </br>@author lzf
 */
public class StudentSortImpl implements Isort {

    @Override
    public int add(int a, int b) {
        int total = a + b;
        (total);
        this.doSomething(a,b);
        return total;
    }

    public static void main(String[] args) {
        // 1.0 Traditional Implementation of Functional Interfaces - Class Implementation
        ("1. Functional Interface Implementation I: Implementing Classes");
        Isort sort = new StudentSortImpl();
        (10, 20);

        // Implementation of Functional Interfaces II - The Langham Approach
        ("2. Functional Interfaces Implementation I: Lambda Expressions");
        // 2.1 In the case of a return, be careful not to return statements, which can only be used in a single statement.
// If there is only one argument, you can omit the parentheses before the ->.
// If there is a return value, in some cases it is also possible to omit the trailing curly braces {}
// When there is a return
// a->a*10
        // (a)->{return a*10} You need to add return if you want to spend brackets.
// (a,b)->a+b
        // (a,b)->{return a+b;}
        Isort sort2 = (a, b) -> a + b;
        Isort sort3 = (a, b) -> {
            return a * 10 + b;
        };

        // 2.2 Is there more than one statement that can be used ->{}
        Isort sort4 = (a, b) -> {
            a += 10;
            return a + b;
        };
        
        int a=10;
        int b=45;
        int total=(a, b)+(a, b)+(a, b);
        ("aggregate="+total);

        // 3 Use new+anonymous functions to implement the
        ("3. Implementation of Functional Interfaces I: Anonymous Classes");
        Isort sort5 = new Isort() {
            @Override
            public int add(int a, int b) {
                int total = a * a + b;
                (total);
                return total;
            }

        };
        (8, 2);

        // 4.0 Based on method references-utilizes existing methods, which must be structured in a way that is consistent with the interface
// In the following example, applying from another class instance that simply implements the methods but not the interface
// It can be speculated that: when compiled, it is implemented by reflection or some way. It depends on the compiled bytecode
        ("4. Functional Interface Implementation I: Method References");
        Sort otherClassSort=new Sort();
        Isort methodSort = otherClassSort::add;
        (90, 90);
        
        // 5.0 Constructor-based
// In this way, the type of the object returned by the constructor should be the same as that returned by the function interface, and of course the parameters should be the same.
        ("5. Implementation of Functional Interfaces I: Constructor References");
        IFace conSort=Face::new;
        
        Face face=(10, 90);
        ();
        //Summary: method-based and constructor-based implementations should be for stream and functional services only, and have little to do with rumination
//This one is most definitely for writing a synopsis-looking expression.
// 6.0 Based on static methods
        ("6. Functional Interface Implementation I: Static Method References");
        Isort staticSort=Integer::sum;
        int total2=(1,2);
        ("total2="+total2);
    }
}
                                  

 

 III. Summary

I was shocked by this confusing implementation of functional interfaces by JCP.

This shock makes me think: I can't rule out the possibility that there may be even more bizarre realizations. If there are, I'll add them later.

Finally, I'm also a bit curious if other commonly used languages have this implementation -- after all, this editor and compiler out of the box.