From: Arved Sandstrom on
Tom Anderson wrote:
> On Mon, 19 Oct 2009, Andreas Leitgeb wrote:
>
>> Marcin Rze?nicki <marcin.rzeznicki(a)gmail.com> wrote:
>>> On 19 Pa?, 17:32, Arved Sandstrom <dces...(a)hotmail.com> wrote:
>>>> Marcin Rze?nicki wrote:
>>>>> On 19 Pa?, 16:13, Andreas Leitgeb <a...(a)gamma.logic.tuwien.ac.at>
>>>>> wrote:
>>>>>> I still see some merit in being able to enforce that any concrete
>>>>>> class implementing some thusly declared interface had to offer some
>>>>>> particular c'tor, as a means to help developers of such classes to
>>>>>> not forget about it.
>>>>> Yep, this is not bad.
>>>> I prefer the annotations-based method such as described here:
>>>> http://www.javaspecialists.eu/archive/Issue167.html
>>> Nice, it wins :-)
>>
>> Indeed nice, but what would be the extra effort to create e.g. a
>> @StringArgConstructor annotation and its processing? And then also
>> a @StringStringArgConstructor and a @StringMyFooIntArgConstructor, ...
>
> Or maybe just a @DefinedConstructor annotation that takes an array of
> classes as a parameter.
>
> tom
>
That's the way I would do it - I managed to get an example of such
working after some anguish (I am not _that_ familiar with the annotation
processing APIs). To use the example provided in my cite, you can pick
off the AnnotationMirror for the Element annotated with
@DefinedConstructor, and use that (to wit, getElementValues()) to
retrieve the classes that you've put into a parameter of the
annotation...this just gets passed down the chain to the visitor. And
down in the visitor, the actual parameters to the ctor are available
via getParameterTypes() on the ExecutableElement.

I find the APIs a bit painful, and maybe others do as well. But on the
plus side, you only need to define a utility annotations processor once,
and then supply it (and others) in a JAR for javac to use. Something
like a @DefinedConstructor annotation _is_ going to cover a lot of
cases, so it's worth the effort, IMHO.

AHS
From: Tomas Mikula on
On Mon, 19 Oct 2009 11:37:07 -0700, Marcin Rzeźnicki wrote:

> On 19 Paź, 19:53, Tomas Mikula <tomas.mik...(a)gmail.com> wrote:
>> > Right but implementation of addition surely checks for this case,
>> > doesn't it?
>>
>> Not necessarily:
>>
>> abstract class Vector<V extends Vector<V>> {
>> public V add(V v);
>>
>> }
>>
>> class Vector2D extends Vector<Vector2D> {
>> private final int x, y;
>> public Vector2D(int x, int y){ this.x = x; this.y = y; } public
>> Vector2D add(Vector2D v){
>> return new Vector2D(this.x + v.x, this.y + v.y);
>> }
>>
>> }
>>
>> No checking that the argument of addition has the correct type, because
>> this is enforced by the compiler.
>>
>>
>>
> Formal arguments have to be invariant with respect to overriding in
> Java, you simply created method overload which will be used when
> compiler is sure that runtime type of argument will be Vector2D. You
> will still have to provide 'generic' add method.

No. Notice how add() is declared in Vector:
public V add(V v);
It works on type V.
When Vector2D is declared as
Vector2D extends Vector<Vector2D>,
Vector2D is substituted for V. So Vector2D's add() method is only
required to work on Vector2D instances. If Vector2D was declared as
Vector2D<V extends Vector<V>> extends Vector<V>,
then Vector2D would have to implement the add() method that takes any
subclass of Vector and returns the same subclass of Vector as it takes.

> Your example does not
> help either (or I cannot see how it would) because you will not be able
> to dispatch on v's actual type unless you change how invokestatic works.

I'm not sure if I understand, but in the implementation of Vector2D V is
bound to Vector2D at compile time.

>> > Yes, consider
>> > public abstract class IOStream //for reading disk streams {
>> > public abstract static boolean isReadable(File f) //returns true
>> > for files which a concrete class can hopefully process. ...
>>
>> > }
>>
>> > public class LocalIOStream extends IOstream { public static boolean
>> > isreadable(File f) { return f.isLocalFile(); } ...
>>
>> > }
>>
>> > public class AudioVideoStream extends LocalIOStream { ???
>>
>> > }
>>
>> > in AVStream you have, if I understood you correctly, two choices -
>> > either to redo all work of super-classes which is not really an
>> > option, let's say,
>> > public static boolean isReadable(File f) { return f.isLocalFile() &&
>> > (f instanceof AudioFile && ((AudioFile)f).getAudioCodecID().equals
>> > (...);}
>>
>> You don't have to redo the work, you can call the superclass's static
>> method as usual:
>>
>> public static boolean isReadable(File f){
>> return LocalIOStream.isReadable(f) &&
>> f instanceof AudioFile &&
>> ((AudioFile)f).getAudioCodecID().equals(...);
>>
>> }
>
> Yeah, right, but consider what happens when someone implements multiple
> interfaces, or when inheritance tree changes, or when someone inherits
> multiple interfaces with conflicting statics and so on.

Right now I can't think of any problems different from those with non-
static methods.

> This example is
> basically hand-crafted implementation of virtual dispatch :-)
>
> ...
>
> Well, ok, but it does not change anything. Still you have to re-
> implement invokevirtual by hand all the time :-)

Well, if you want to extend the behavior of a non-static method, you
still have to call the inherited method by hand (super.someMethod()) and
then do your specific work. This is no different from my example. The
only real disadvantage is when you want to inherit the method as is. Yes,
this is not possible with my proposal and you have to call the
superclass's method by hand. Though my proposal was targeted at uses
where you want to provide own implementation in each class. But I'm
finally getting what you are trying to say - that this feature might
encourage design that will later turn out as wrong. Yes, there might be
this danger, as is with many other features.

>> > or omit it so then you impose different context. Namely, pretend to
>> > be able to read remote files while you are not. And one more
>> > question:
>> > //client code
>> > Stream s = new AudioVideStream(..);
>> > read10Bytes(s);
>>
>> > public byte[] read10Bytes(Stream s) { if (!Stream.isReadable(file))
>> > //how would you dispatch it? There is no way I suppose
>>
>> > }
>>
>> This would be a compile-time error, since isReadable() is abstract in
>> Stream.
>
> This is really bad :-) Then actually your statics will be usable only
> when you know exact type you want to work with.

And with generics and with dynamic loading.
From: Arved Sandstrom on
Tom Anderson wrote:
[ SNIP ]

> Now, i should say that that is some fairly bonkers generics (in fact, i
> think i can now claim Double Wizard status at generics). It took me a
> good while to get the types straight, and even now, the declarations are
> of a mind-searing eldritch uncanniness verging on the Lovecraftian.
[ SNIP ]

I'll have to use that theme at a code review:

"This tenebrous source, wrought of a minion of Azathoth, must be
consigned to the subterranean depths ere this codebase suffers the fate
of all software that embraces the ultimate mindless chaos."

AHS
From: Tomas Mikula on
On Mon, 19 Oct 2009 21:05:05 +0100, Tom Anderson wrote:

> On Mon, 19 Oct 2009, Tomas Mikula wrote:
>
>> Now I present two examples where it would be useful.
>>
>> (1) Eliminate or reduce the use of reflection in serialization
>> frameworks.
>
> This example isn't actually about your proposal, it's about ...
>
>> a new magic class Implementation<T>
>
> ... so i'm going to ignore it.
>
>> (2) The second use case is with generics, but would require reified
>> generics (which I hope will appear in some future version of Java).
>>
>> Suppose you have an abstract class Vector which represents a vector in
>> a vector space (don't confuse with java.util.Vector) and a few
>> implementations, like Vector2D and Vector3D. [...] Now let's have a
>> generic class that will use vectors and do operations on them, but
>> doesn't really care about their actual dimension. So it will work with
>> abstract type Vector. But for some operations it may be necessary to
>> obtain the zero vector, without explicitely knowing the actual type of
>> vector.
>
> This is a good use case. But i suspect it can be done with java as it
> stands now - rather than reified generics, you factor out a Type Object,
> as the patternists call it. Vectors can tell you their type object, and
> the type object can give you a zero.
>
> Like this, for instance:
>
> http://urchin.earth.li/~twic/Code/MutuallyRecursiveGenerics/Vector.java

The VectorKind should also have two type parameters to get rid of
compiler warnings and to map Vectors to VectorKinds one-to-one:

abstract class
VectorKind<V extends Vector<V,K>, K extends VectorKind<V,K>>

Anyway, this is not of any help for me, because you are obtaining the
VectorKind instance from an instance of Vector. If there is always an
instance of Vector at hand, I can obtain the zero directly from that
instance (just move the abstract zero() method you declared in VectorKind
to Vector). The problem is that there is not always an instance at hand
and it is cumbersome to artificially pass one. The result is that the
readability of code suffers a lot, because it contains the workarounds,
not the actual work.
From: Tom Anderson on
On Mon, 19 Oct 2009, Tomas Mikula wrote:

> On Mon, 19 Oct 2009 21:05:05 +0100, Tom Anderson wrote:
>
>> http://urchin.earth.li/~twic/Code/MutuallyRecursiveGenerics/Vector.java
>
> The VectorKind should also have two type parameters to get rid of
> compiler warnings and to map Vectors to VectorKinds one-to-one:
>
> abstract class
> VectorKind<V extends Vector<V,K>, K extends VectorKind<V,K>>

It compiled without warnings for me - but then i'm on 1.5.

> Anyway, this is not of any help for me, because you are obtaining the
> VectorKind instance from an instance of Vector. If there is always an
> instance of Vector at hand, I can obtain the zero directly from that
> instance (just move the abstract zero() method you declared in
> VectorKind to Vector). The problem is that there is not always an
> instance at hand and it is cumbersome to artificially pass one.

What are you planning to do with this zero if you don't have any Vector
instances? I don't really buy your scenario.

tom

--
NO REAL THAN YOU ARE -- Ego Leonard, The Zandvoort Man