Anonymous subclassing of Callable<?> for calling super()

  • Thread starter Christopher Benson-Manica
  • Start date
C

Christopher Benson-Manica

Recently, it chanced that some code like this was written:

public class Bar extends Foo {
// Foo has one constructor, taking a List<String> as an argument
public Bar(final Object... args) {
super( new Callable<List<String>> () {
public List<String> call() {
final List<String> list = new LinkedList<String>();
// Transform args into String's and put them in the list
return list;
}
}.call() );
}
}

The motivation is to transform Foo's constructor interface into
something that is more convenient for Bar's clients. This strikes me
as grievously hacky, but I also don't see another way to accomplish
this aside from refactoring, which may not even be possible depending
on where Foo comes from. Is this a reasonable (or at least accepted)
idiom for accomplishing this goal, or is there a more elegant
solution?
 
D

Daniel Pitts

Recently, it chanced that some code like this was written:

public class Bar extends Foo {
// Foo has one constructor, taking a List<String> as an argument
public Bar(final Object... args) {
super( new Callable<List<String>> () {
public List<String> call() {
final List<String> list = new LinkedList<String>();
// Transform args into String's and put them in the list
return list;
}
}.call() );
}

}

The motivation is to transform Foo's constructor interface into
something that is more convenient for Bar's clients. This strikes me
as grievously hacky, but I also don't see another way to accomplish
this aside from refactoring, which may not even be possible depending
on where Foo comes from. Is this a reasonable (or at least accepted)
idiom for accomplishing this goal, or is there a more elegant
solution?

It seems a bit excessive to create a whole new object, just to call a
method on it. How about a static method?

public class Bar extends Foo {
// Foo has one constructor, taking a List<String> as an argument
public Bar(final Object... args) {
super( convert(args) );
}

private static List<String> convert(Object...args) {
final List<String> list = new LinkedList<String>();
// Transform args into String's and put them in the list
return list;
}
}
 
R

Robert Klemme

Recently, it chanced that some code like this was written:

public class Bar extends Foo {
// Foo has one constructor, taking a List<String> as an argument
public Bar(final Object... args) {
super( new Callable<List<String>> () {
public List<String> call() {
final List<String> list = new LinkedList<String>();
// Transform args into String's and put them in the list
return list;
}
}.call() );
}
}

The motivation is to transform Foo's constructor interface into
something that is more convenient for Bar's clients. This strikes me
as grievously hacky, but I also don't see another way to accomplish
this aside from refactoring, which may not even be possible depending
on where Foo comes from. Is this a reasonable (or at least accepted)
idiom for accomplishing this goal, or is there a more elegant
solution?

You do not show Foo but as Daniel said already, basically you are
invoking Foo(List<String> list). The situation would be different if
you did not invoked call() and just handed the Callable to Foo's
constructor. Having said that, yes, use a static method as Daniel
pointed out already.

Kind regards

robert
 
T

Tom Hawtin

Daniel said:
It seems a bit excessive to create a whole new object, just to call a
method on it. How about a static method?

Or use a static creation method:

public class Bar extends Foo {
public static create(Object... args) {
List<String> list = new java.util.ArrayList<String>();
// LinkedList is almost always a bad idea.
// Use Arrays.asList if you want to be uber efficient.
...
}
private Bar(List<String> args) {
super(args);
}
...

Also Callable is defined to throw Exception.

Tom Hawtin
 
C

Chris Uppal

Christopher said:
Recently, it chanced that some code like this was written:

public class Bar extends Foo {
// Foo has one constructor, taking a List<String> as an argument
public Bar(final Object... args) {
super( new Callable<List<String>> () {
public List<String> call() {
final List<String> list = new LinkedList<String>();
// Transform args into String's and put them in the list
return list;
}
}.call() );
}
}

God help us -- that's /vile/ !

The motivation is to transform Foo's constructor interface into
something that is more convenient for Bar's clients. This strikes me
as grievously hacky, but I also don't see another way to accomplish
this aside from refactoring, which may not even be possible depending
on where Foo comes from.

I'd use a static helper. Or, if that turned out to be impossible, take it as
a very strong indication that the design was a pile of crap and start again.

BTW, I question whether that idiom is really legal. Or, if it should turn out
to be within the letter of the JLS law, whether it /should/ be legal.

The problem is that it makes use of a reference to the object under
construction (using it to create the instance of the inner class) before the
superclass's constructor has been invoked. That is forbidden in every other
part of the Java design. It is OK, however foolish, to pass around references
to "this" after the superclass constructor has returned, but before your own
constructor is complete; but it is /not/ OK to do so before calling the
superclass constructor.

In fact I'm somewhat surprised that it passed verification in the JVM -- there
are strict rules about what you can do with a reference to a allocated-but-not
fully-initialised object, and until the call to the superclass constructor has
returned, "this" is in an illegal state.

-- chris
 
C

Christopher Benson-Manica

Daniel Pitts said:
It seems a bit excessive to create a whole new object, just to call a
method on it. How about a static method?

Sounds good to me, that was the insight I was looking for. Thanks.
 
C

Christopher Benson-Manica

Chris Uppal said:
God help us -- that's /vile/ !

Well, I wasn't a big fan either, although I did have to be a bit more
diplomatic about things :) (Perhaps tellingly, a slight elaboration
on the code in question also exposed a flaw in IntelliJ's code
inspection - clearly it's at least uncommonly used enough that the bug
escaped notice until now!)
The problem is that it makes use of a reference to the object under
construction (using it to create the instance of the inner class) before the
superclass's constructor has been invoked.

I'm still fuzzy on some of the things that lurk hidden from the eyes
of the casual programmer - but yes, now that you mention it, it's
obvious.
In fact I'm somewhat surprised that it passed verification in the JVM -- there
are strict rules about what you can do with a reference to a allocated-but-not
fully-initialised object, and until the call to the superclass constructor has
returned, "this" is in an illegal state.

It seems that the Java compiler is smart enough to determine when
you've clearly overstepped your bounds - say by accessing a field in
the subclass before the superclass constructor has been called - and
when you've merely committed stylistic seppuku.
 
T

Tom Hawtin

Chris said:
BTW, I question whether that idiom is really legal. Or, if it should turn out
to be within the letter of the JLS law, whether it /should/ be legal.

It's certainly an area where the JLS and javac have been at odds in the
past. In this case, javac seems to consider the nested class to be in a
static context. At least that is consistent with the output of javap -c
on 1.6.0 u1 ea b03 (other builds may do something different).

Tom Hawtin
 
C

Chris Uppal

Tom Hawtin wrote:

[me:]
It's certainly an area where the JLS and javac have been at odds in the
past. In this case, javac seems to consider the nested class to be in a
static context.

So it does ! Viler and viler...

(Though, to be honest, that does slightly reduce my distaste for the original
trick -- at least the code /is/ static, for all it doesn't look it.)

-- chris
 
T

Tom Hawtin

Chris said:
So it does ! Viler and viler...

(Though, to be honest, that does slightly reduce my distaste for the original
trick -- at least the code /is/ static, for all it doesn't look it.)

The trick looks reasonable, until you realise it isn't doing quite what
you expected.

Anyway, I was looking for this bug in particular:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6226815

The example of an anonymous inner class within an inner class
constructor given in the JLS which isn't supposed to compile, does
(unless -target 1.4 or earlier is used, apparently).

Note the bug description says the code is from JLS2 (8.8.5.1) and the
bug is closed on that basis. However 8.8.7.1 (p245) of JLS3 has the same
code (the maintenance review has a mark against it, but doesn't appear
to have changed anything). I guess that bug should be reopened.

http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#229267
http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.8.7.1

If only JDKs had version number 0.1 (or 0.2) lower, nested classes could
have been a VM-level concept.

Tom Hawtin
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top