Myridium
Myridium

Reputation: 904

Passing Anonymous Classes

This is a question for Java. I have an interface called IMyObjectPredicate which implements a single test method to apply to an input:

public interface IMyObjectPredicate {
    boolean test(MyObject x);
}

What I would like is to be able to pass an instance of IMyObjectPredicate around between objects and have the test function update its references to variables to those of the new object it is being passed to. For instance, consider a class which makes use of this predicate:

public class Tester {
    MyObject o;
    IMyObjectPredicate myTestFunction;
    int myThreshold;

    public Tester(/*stuff*/) {
        /*Code here which initialises the Tester instance and 'myThreshold'*/
        myTestFunction = new IMyObjectPredicate() {
            @Override
            public boolean test(MyObject o) {
                return (o.value() > myThreshold);
            }
        };
    }

    public boolean isGood() {
        return myTestFunction.test(o);
    }
}

I would like to be able to perform a deep clone of the Tester object for reasons I won't go into here. But the idea is that the cloned instance of Tester should test the predicate against its own value of myThreshold, not reference the myThreshold of the first instance. But if I pass myTestFunction to a new instance of Tester, I guess it will still be referencing the myThreshold value of the first instance, instead of dynamically evaluating myThreshold based on the reference of the enclosing class.

How can I accomplish the passing of a IMyObjectPredicate object whose test function uses references to the fields of the new object it is passed to?

Edit: A complicating factor is that, in general, it will not be possible to reconstruct myTestFunction solely from the fields within a Tester object. myTestFunction may be overwritten by other parts of the program in a way that does not correlate with the other fields of Tester. I can sacrifice this functionality if need be, but I would rather not for the sake of elegance.

Upvotes: 0

Views: 247

Answers (2)

Mshnik
Mshnik

Reputation: 7032

It is a lot easier if ImObjectPredicate is an class that simply stores a reference to a predicate instead of an interface. If you're able to make that change, each predicate can store its own threshold, which solves the issue.

public IMyObjectPredicate {
    private int threshold;
    private Predicate<MyObject> pred;

    public int getThreshold() {
        return threshold;
    }

    public Predicate<MyObject> getPredicate() {
        return pred;
    }

    public IMObjectPredicate(int threshold, Predicate<MyObject> pred) {
        this.threshold = threshold;
        this.pred = pred;
    }

    public boolean test(MyObject o) {
        return pred.test(o);
    }
}

public class Tester {
    MyObject o;
    IMyObjectPredicate myTestFunction;
    IMyObjectPredicate myTestFunctionCopyWithDiffThreshold;
    int myThreshold;

    public Tester(/*stuff*/) {
        /*Code here which initialises the Tester instance and 'myThreshold'*/
        myTestFunction =
            new IMyObjectPredicate(myThreshold, o -> o.value() > getThreshold());
        myTestFunctionCopyWithDiffThreshold =
            new ImObjectPredicate(5, myTestFunction.getPredicate());
    }

    public boolean isGood() {
        return myTestFunction.test(o);
    }
}

This is the most sensible solution, as ImObjectPredicate should store its own threshold if that value uniquely refers to that ImObjectPredicate.

Upvotes: 0

Nazarii Bardiuk
Nazarii Bardiuk

Reputation: 4342

Java does not have an API to replace enclosed context of anonymous class.

The simplest solution I can see from your simplified example is to add threshold to the signature of test function. As I understand the threshold is going to be there anyway.

public interface IMyObjectPredicate {
    boolean test(MyObject x, int threshold);
}

Another approach would use some factory method that will create a predicate for provided threshold like

class PredicateFactory {
    IMyObjectPredicate thresholdPredicate(int threshold) {
        return new IMyObjectPredicate {
              //...
        }
    }
}

then you can pas this factory to object that will use it's own threshold to construct new instance of predicate

factory.thresholdPredicate(myThreshold);

Upvotes: 2

Related Questions