use of covariance - java

Started by
6 comments, last by dark_696 19 years, 1 month ago
As I understand covariance is the ability of overrided class functions to return type that is inherited from the base class. What I don't understand is the use of that. I mentioned java in the thread name because java natively supports covariance, opposed to c++ where I read (I could be wrong) that covariance is gained through use of templates. Anyway, can anyone explain some practical use of it?
Advertisement
Quote:Original post by dark_696
I mentioned java in the thread name because java natively supports covariance, opposed to c++ where I read (I could be wrong) that covariance is gained through use of templates.


Yes your very wrong, standard C++ always had covariant return types you do not need to use templates to use it, where as java only introduce them in 1.5.
Nice :-)

Can you please explain what is its use and did I define covariance completely and correctly?
It would be quite a useless limitation to not allow an overridden method to return types derived from the original one. Imagine you had a virtual file system with a class called Archive that returns streams:
class Archive {  Stream *openStream(const string &sName) = 0;};class DirectoryArchive : public Archive {  FileStream *openStream(const string &sName) { /* ... */ }};

As long as you're obtaining an abstract Archive pointer from somewhere, you've got to work with just as abstract Streams.

But if you had constructed a DirectoryArchive yourself, it would be clear that it would be returning FileStreams, not ZipStreams or something else. Without covariants, an ugly downcast would be required, should you ever want to access some special method only available in the FileStream class.

-Markus-
Professional C++ and .NET developer trying to break into indie game development.
Follow my progress: http://blog.nuclex-games.com/ or Twitter - Topics: Ogre3D, Blender, game architecture tips & code snippets.
I always thought of it in other form. More like this (I'll write in Java):

public interface Super {    Super getInstance();}public class Child implements Super {    @Overrides Child getInstance() {         return new Child();    }}


Now... If I use Super somewhere, what use is if I don't know what is implementation?

Or, if I use Child, why do I need interface, or why do I need method which returns new instance, when I can use constructor then?

I thought covariance is somehow a safe way for super type to dynamically cast to child type, so I can get a pointer to super, and then find out what implementation it is and avoid chain of instanceof checks.

Oh, I realized, is it maybe good for, example:

public class Super {
public Super doSomethingAndReturn() {
// do something
return newInstance;
}
}

public class Child extends Super {
public Child doSomethingAndReturn() {
super.doSomethingAndReturn();
// do something
return newInstance;
}
}

Calling super to initialize object and then add some more inits and returning child type? But its not so useful as I can extend only one class, so we avoid using classes and restrict as much as we can to interfaces.
Quote:Original post by dark_696
Nice :-)

Can you please explain what is its use and did I define covariance completely and correctly?


Yes as far as i'm aware of its a form of covariance, its main points are:

A. Keeping the exact type till the very last moment of disregarding specific types, this gives a degree of type saftefy and gives the compile a chance to catch mistakes.

B. Avoid the need for type casting.

E.g. in java (my java is probably a little rusty..), say you have an interface that requires all implementors to be cloneable:

public interface Bar {   Bar cloneBar();}public final class Foo implements Bar {   public int bar;   Foo(int i) {      bar = i;   }   Foo(final Foo f) {      this(f.bar);   }   public Foo cloneBar() {       return new Foo(this);   }   public static void main(String[] args) {       Foo f = new Foo(10);       Foo f2 = f.cloneBar();   //covariant return types allows you to do this                            //with-out the need to type cast from the super type to sub-types.       Bar c1 = f.cloneBar();       System.out.println(((Foo)c1).bar);       Bar c2 = c1.cloneBar();       System.out.println(((Foo)c2).bar);   }}


With-out covariant return types the assignement of f2 would require type casting.

For the sake of arguement the C++ version would be:

struct bar {   virtual bar* clone_bar() const = 0   virtual ~bar() {}};struct foo : bar {   int i;   foo(int j = 0): i(j) {}   foo* clone_bar() const { return new foo(*this); }};#include <memory>int main() {   typedef std::auto_ptr<foo> foo_ptr;   typedef std::auto_ptr<bar> bar_ptr;   foo f = 10;   foo_ptr f2(f.clone_bar());   bar_ptr  b(f2->clone_bar());   bar_ptr  b2(b->clone_bar());}
My, now I feel stupid. I use covariance all the time and didn't have a clue I do :-)

Thanks.
Well, then again, when we're at it, what is contravariance and is it supported in C++?

This topic is closed to new replies.

Advertisement