10

I wanna implement a javascript like method in java , is this possible ?

Say , I have a Person class :

public class Person {
 private String name ;
 private int age ;
 // constructor ,accessors are omitted
}

And a list with Person objects:

Person p1 = new Person("Jenny",20);
Person p2 = new Person("Kate",22);
List<Person> pList = Arrays.asList(new Person[] {p1,p2});

I wanna implement a method like this:

modList(pList,new Operation (Person p) {
  incrementAge(Person p) { p.setAge(p.getAge() + 1)};
});

modList receives two params , one is a list , the other is the "Function object", it loops the list ,and apply this function to every element in the list. In functional programming language,this is easy , I don't know how java do this? Maybe could be done through dynamic proxy, does that have a performance trade off compares to native for loop ?

Sawyer
  • 15,581
  • 27
  • 88
  • 124

5 Answers5

14

You can do it with an interface and an anonymous inner class implementing it, like

Person p1 = new Person("Jenny",20);
Person p2 = new Person("Kate",22);
List<Person> pList = Arrays.asList(p1, p2);

interface Operation {
  abstract void execute(Person p);
}

public void modList(List<Person> list, Operation op) {
  for (Person p : list)
    op.execute(p);
}

modList(pList, new Operation {
  public void execute(Person p) { p.setAge(p.getAge() + 1)};
});

Note that with varargs in Java5, the call to Arrays.asList can be simplified as shown above.

Update: A generified version of the above:

interface Operation<E> {
  abstract void execute(E elem);
}

public <E> void modList(List<? extends E> list, Operation<E> op) {
  for (E elem : list)
    op.execute(elem);
}

modList(pList, new Operation<Person>() {
    public void execute(Person p) { p.setAge(p.getAge() + 1); }
});

Note that with the above definition of modList, you can execute an Operation<Person> on e.g. a List<Student> too (provided Student is a subclass of Person). A plain List<E> parameter type would not allow this.

Péter Török
  • 114,404
  • 31
  • 268
  • 329
3

Take a look at the lambdaj project. Here is an example from the project home page:

List<Person> personInFamily = asList(new Person("Domenico"), new Person("Mario"), new Person("Irma"));
forEach(personInFamily).setLastName("Fusco");
Andrea Polci
  • 1,011
  • 13
  • 27
2

Look at the google collections library. Look at the transform methods on Iterators and Iterables. That should be able to get you what you want.

Russell Leggett
  • 8,795
  • 3
  • 31
  • 45
1

Yes, this is easy in a functional programming language.. in Java it's a little more complex but you can work out something like this, using also generics types when possible:

public class TestCase {
    static interface Transformable {};

    static class Person implements Transformable {
        Person(String name, int age) {
            this.name = name;
            this.age = age;
        }

        public String name;
        public int age;
    }

    static interface Modifier<T extends Transformable> {
        void modify(Transformable object);
    }

    static class AgeIncrementer implements Modifier<Person> {
        public void modify(Transformable p) {
            ++((Person)p).age;
        }
    }

    static void applyOnList(List<? extends Transformable> objects, Modifier<? extends Transformable> modifier) {    
        for (Transformable o : objects) {
            modifier.modify(o);
        }
    }

    public static void main(String[] args) {
        ArrayList<Person> l = new ArrayList<Person>();
        l.add(new Person("John", 10));
        l.add(new Person("Paul", 22));
        l.add(new Person("Frank", 35));

        applyOnList(l, new AgeIncrementer());

        for (Person p : l)
            System.out.println(p.age);
    }
}
Jack
  • 131,802
  • 30
  • 241
  • 343
  • `Modifier.modify` should have a parameter of type `T` instead of `Transformable` to make downcasts unnecessary and increase type safety. – Péter Török Apr 08 '10 at 19:41
0
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FuntionAsParameter {


    public static void main(final String[] args) {

        Person p1 = new Person("Jenny", 20);
        Person p2 = new Person("Kate", 22);

        List<Person> pList = Arrays.asList(new Person[]{p1, p2});

        Function<List<Person>, List<Person>> theFunction = Function.<List<Person>>identity()
            .andThen(personList -> personList
                .stream()
                .map(person -> new Person(person.getName(), person.getAge() + 1))
                .collect(Collectors.toList()));

        // You can use this directly
        List<Person> directly = theFunction.apply(pList);
        directly.forEach(person -> System.out.println(person));

        // Or use it as an input parameter
        List<Person> toMethod = modList(pList, theFunction);
        toMethod.forEach(person -> System.out.println(person));

        // Or you might prefer this way
        List<Person> thirdWay = modList(pList,
                (list) -> list.stream()
                        .map(person -> new Person(person.getName(), person.getAge() + 1))
                        .collect(Collectors.toList())
        );
        thirdWay.forEach(person -> System.out.println(person));
    }

    private static List<Person> modList(List<Person> personList, Function<List<Person>, List<Person>> theFunction) {
        return theFunction.apply(personList);
    }


}

class Person {

    private String name;
    private int age;

    public Person(final String name, final int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" +
            "name='" + name + '\'' +
            ", age=" + age +
            '}';
    }
}