Supply super constructor with lambda expression that captures `this`

39 views
Skip to first unread message

zhong...@gmail.com

unread,
Jun 15, 2015, 9:53:31 PM6/15/15
to java-la...@googlegroups.com
Suppose we had an abstract class with a single abstract method

    abstract public class Foo
    {
        public Foo(){}
        abstract protected String bar();
    }

and we refactor it to a normal class that accepts a functional object instead

    public class Foo
    {
        final Supplier<String> bar;
        public Foo(Supplier<String> bar){ this.bar = bar; }
    }

That is all good; we can do `new Foo( ()->"whatever" )`, no subclassing is needed!

However, what if, for some reason, we do want to subclass Foo?

    public class MyFoo extends Foo
    {
        final int x;
        public MyFoo(int x)
        {
            super( this::bar ); // ERROR! `this` cannot be referenced here
            this.x=x;
        }

        String bar(){ return ""+x; }
    }

There is no way to supply a lambda that depends on `this` to the super constructor. Java is very good at forbidding `this` from being accessed before super is constructed.

The only workaround that I can come up with is to let a super constructor to do a downcast of `this` then bind the lambda expression to it

    public class MyFoo extends Foo
    ...
        public MyFoo(int x)
        {
            <MyFoo>super( thiz->thiz::bar );
            this.x=x;
        }


    public class Foo
    ...
        @SuppressWarnings("unchecked")
        protected <This> Foo(Function<This,Supplier<String>> barF)
        {
            this.bar = barF.apply((This)this);
        }


Zhong Yu
bayou.io

y s

unread,
Jun 16, 2015, 12:55:55 AM6/16/15
to zhong...@gmail.com, java-la...@googlegroups.com
I think there are good reasons for Java to prevent that second bit of code. If the super constructor invoked the lambda (rather than just storing it for future use), and if that lambda used state set in the subclass, then the super constructor would get the wrong value. This is essentially the extension of the advice to never call virtual (non-final, non-private, non-static) methods from a constructor. In the example you give, if you invoke "new MyFoo(27)", and the super pre-computed the value by initializing a "private final String s" to "bar.get()", then you'd get s = "0" instead of the expected s = "27".

--
You received this message because you are subscribed to the Google Groups "java.lang.fans" group.
To unsubscribe from this group and stop receiving emails from it, send an email to java-lang-fan...@googlegroups.com.
To post to this group, send email to java-la...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/java-lang-fans/23f66ccf-9e82-49c6-9625-c5ad42266603%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Zhong Yu

unread,
Jun 16, 2015, 9:25:10 AM6/16/15
to y s, java-lang-fans

On Mon, Jun 15, 2015 at 11:55 PM, y s <eleus...@gmail.com> wrote:
I think there are good reasons for Java to prevent that second bit of code. If the super constructor invoked the lambda (rather than just storing it for future use), and if that lambda used state set in the subclass, then the super constructor would get the wrong value. This is essentially the extension of the advice to never call virtual (non-final, non-private, non-static) methods from a constructor. In the example you give, if you invoke "new MyFoo(27)", and the super pre-computed the value by initializing a "private final String s" to "bar.get()", then you'd get s = "0" instead of the expected s = "27".


Yes. It has to be part of the constructor contract when and how a functional object will be invoked. For example, in `Thread(Runnable)`, `ThreadLocal.withInitial(Supplier)`.

If a function may be invoked in a constructor, it is almost certainly a mistake to design an abstract method to provide the function. On the other hand, taking in the function as an argument to the constructor *absolutely* (as far as I can tell) prevent subclass from submitting a function that depends on subclass state; as long as the superclass does not purposefully provide a circumvention device for subclass like the `protected Foo(Function<This,Supplier>)`

Zhong Yu

zhong...@gmail.com

unread,
Sep 8, 2015, 3:46:33 PM9/8/15
to java.lang.fans


On Monday, June 15, 2015 at 8:53:31 PM UTC-5, zhong...@gmail.com wrote:

There is no way to supply a lambda that depends on `this` to the super constructor. Java is very good at forbidding `this` from being accessed before super is constructed.


However, the "outer this" is accessible before super(); so a workaround can be structured as out-inner class

    public class Outer
    {
        final int x;
        public Outer(int x)
        {

            this.x=x;
        }
        String bar(){ return ""+x; }

        public class MyFoo extends Foo
        {
            public MyFoo()
            {
                super(Outer.this::bar);
            }
        }
    }

Which can be used as

        Foo foo = new Outer(42).new MyFoo();
        System.out.println(foo.bar.get());  // 42

The "outer" instance is constructed even before the super instance; this might be useful in situations. See also http://stackoverflow.com/a/32465492/2158288

 

Zhong Yu
bayou.io
Reply all
Reply to author
Forward
0 new messages