From: Marcin Rzeźnicki on
On 20 Paź, 00:29, Tomas Mikula <tomas.mik...(a)gmail.com> wrote:
> 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.
>

Sorry, I did not notice - compiler probably is bound to generate
bridge method which makes cast to Vector2D

> > 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.
>

But I am free to pass an instance of any conforming type.

>
>
> >> > 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.
>

Except that super calls are dispatched by VM with no help needed from
a programmer.

> > 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.
>
>

Right, but the difference is that super calls are not subject to
'human' errors (what happens if inheritance chain has been updated?
you have to manually review all subclasses and repair static calls)

>
> >> > 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.

Still, you have to redefine static method so that it is _usable_ for
every type, but _used_ only when exact type is of special interest
which encourages too strict typing. Ok, probably now I understand
fully your concept. I'd uphold my opinion that this is not so good.
Here is a quick rundown why I think so:
1. Problem it solves can be solved in other ways, without any need for
special syntax/semantics/language or VM changes (like Vector)
2. It is not clear how it would/should behave even for problems it is
supposed to solve
(code: public <T extends Foo> void doSth(T t) { T.staticFromFoo(); }
public class Bar extends Foo { public static void staticFromFoo()
{...};}
what should it bind to? If Foo, what if it is passed a Bar instance in
runtime?)
3. It encourages bad code smell, that is: coding with the most
specific type instead of coding with interfaces
4. It solves relatively infrequent problems
5. It is error prone - developers will have to study all subclasses to
spot what should be changed after any superclass has changed
6. It makes developers implement this static heritage over and over,
even when it is not needed

Please clarify if I am wrong somewhere.
From: Daniel Pitts on
Tomas Mikula wrote:
> On Sun, 18 Oct 2009 19:19:55 -0700, Peter Duniho wrote:
>
>> Tomas Mikula wrote:
>>> I have searched this group for "abstract static methods" and found a
>>> couple of threads, but I think none of them was discussing the kind of
>>> semantics I am going to describe. As you might have guessed, I believe
>>> it would be useful :). [...]
>> You aren't the first. However, in C++, C#, Java, and languages like
>> them, you simply are never going to have methods that are both virtual
>> and static. And since abstract implies virtual, that rules out abstract
>> static methods too.
>
> I'm not talking about virtual static methods. In this case, abstract
> would not imply virtual. My second use case could most probably be
> accomplished in C++ by Concepts (though Concepts didn't make it to C+
> +0x). In fact, it is achievable with current C++ templates, only without
> compile time checking --- it is possible to call T::staticMethod() where
> T is a template parameter.
C++ Template programming provides compile-time polymorphism. In Java,
you don't have Templates, so there is no similar approach.

If you think you need a "static" method to be overidden, then you really
need to have a non-static method, on a polymorphic object.

--
Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
From: Tomas Mikula on
On Tue, 20 Oct 2009 00:05:41 +0100, Tom Anderson wrote:

> 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.

I haven't tried to compile it, but I suppose that the compiler should
issue a warning on line
abstract class VectorKind<V extends Vector>
because Vector is a raw type.

>> 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.

Consider you want to implement a method that takes a collection of
vectors and returns their sum. Simple, eh? If I can call static methods
on type parameters, it will look like this:

public V sum(Collection<V> coll){
V s = V.zero();
for(V v: coll)
s = s.add(v);
return s;
}

However, we cannot call V.zero(). What will you return if coll is an
empty collection?
From: Marcin Rzeźnicki on
On 20 Paź, 01:45, Tomas Mikula <tomas.mik...(a)gmail.com> wrote:
> On Tue, 20 Oct 2009 00:05:41 +0100, Tom Anderson wrote:

>
> > What are you planning to do with this zero if you don't have any Vector
> > instances? I don't really buy your scenario.
>
> Consider you want to implement a method that takes a collection of
> vectors and returns their sum. Simple, eh? If I can call static methods
> on type parameters, it will look like this:
>
> public V sum(Collection<V> coll){
>     V s = V.zero();
>     for(V v: coll)
>         s = s.add(v);
>     return s;
>
> }
>
> However, we cannot call V.zero(). What will you return if coll is an
> empty collection?

But what is so wrong with passing neutral element to the method and
implement it like foldl? For me it is quite natural

From: Tomas Mikula on
On Mon, 19 Oct 2009 16:06:27 -0700, Marcin Rzeźnicki wrote:

> On 20 Paź, 00:29, Tomas Mikula <tomas.mik...(a)gmail.com> wrote:
>> 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.
>>
>>
> Sorry, I did not notice - compiler probably is bound to generate bridge
> method which makes cast to Vector2D
>
>> > 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.
>>
>>
> But I am free to pass an instance of any conforming type.

I don't think so. At least not without the compiler warning about
unchecked casting. If you have
Vector2D v;
you can only pass Vector2D instances to v.add(...). If you have
V v;
where V is actually Vector2D, you can only pass V instances to
v.add(...). But V is actually Vector2D, so it is OK.

>> >> > 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.
>>
>>
> Except that super calls are dispatched by VM with no help needed from a
> programmer.

The thing is that prohibiting inheritance is a feature here, not a
drawback. This is because inherited behavior is most likely to be wrong.
(as with Vector.zero() or Serializable.readObject() (if readObject was a
static method returning the read object)). If this is not the case, then
it would probably not be the best choice to use this feature.

>> > 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.
>>
>>
>>
> Right, but the difference is that super calls are not subject to 'human'
> errors (what happens if inheritance chain has been updated? you have to
> manually review all subclasses and repair static calls)

This is not a new problem. You have the same problem with current static
methods, if you call a static method of a superclass. It might be useful
to have some static analog of the 'super.instanceMethod()' construct,
regardless of my proposal.

>> >> > 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.
>
> Still, you have to redefine static method so that it is _usable_ for
> every type, but _used_ only when exact type is of special interest which
> encourages too strict typing.

Again not sure what exactly you meant. In the example of AudioVideoCodec
I provided, the isReadable() method was only required to work with
AudioFiles.

> Ok, probably now I understand fully your
> concept. I'd uphold my opinion that this is not so good. Here is a quick
> rundown why I think so: 1. Problem it solves can be solved in other
> ways, without any need for special syntax/semantics/language or VM
> changes (like Vector)

Each problem can be solved in other ways. You could argue that we don't
need generics, because every problem can be solved without them. The
question is how concise and readable the solution is. Unnatural hacks are
IMO very bad for code readability.

> 2. It is not clear how it would/should behave even
> for problems it is supposed to solve
> (code: public <T extends Foo> void doSth(T t) { T.staticFromFoo(); }
> public class Bar extends Foo { public static void staticFromFoo()
> {...};}
> what should it bind to? If Foo, what if it is passed a Bar instance in
> runtime?)

It should bind to whatever type T represents. That is why reified
generics are necessary. The bytecode of doSth() would contain the
information that the staticFromFoo() method of T is called, where T will
be specified at runtime (though I suspect that this could be optimized at
link-time).

> 3. It encourages bad code smell, that is: coding with the most specific
> type instead of coding with interfaces

How? Did we have an example of this? If yes, I probably missed it.

> 4. It solves relatively infrequent problems

The problem with serialization frameworks is infrequent. I suspect that
the other problem with generics may be quite frequent.

> 5. It is error prone - developers will have to study
> all subclasses to spot what should be changed after any superclass has
> changed

I don't see any special relation of this problem to the proposed feature.
It is always bad if you depend on the implementation of the superclass
rather than on the interface only. This feature does not encourage you in
any way to depend on the superclass. If you are referring to the problem
of calling superclass methods on superclass's class literal
(MySuperClass.staticMethod()), I already argued that this is not a new
problem.

> 6. It makes developers implement this static heritage over and
> over, even when it is not needed

It is designed for cases when it _is_ needed. I admit that someone could
be tempted to use it inappropriately. But almost anything can be misused.