[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openjdk-lambda-dev
Subject:    Interface evolution via virtual extension methods - Extending Interfaces By Breaking Application Log
From:       elvis_ligu () hotmail ! com (Elvis Ligu)
Date:       2012-03-15 16:14:10
Message-ID: BAY154-W785E8C41704E222F1DB69E25E0 () phx ! gbl
[Download RAW message or body]


*Am I seeing some ironic in your email? If so, than my reply wasn't to be ironic, but \
to explain my thoughts.I have some problems with my English so forgive me if I look \
like like being rude. I have difficulties to explain it better, so excuse me again, \
it wasn't my intention.

> I recognized by 3) or 4) that all of calling for "overrid"den method will be a \
> cause of UnsupportedOperationException. 
You are totally right, 3) and 4) are somewhat misleading. Actually 1), 2), 3)  and so \
on, are just suggestions and they are only provided as ideas of what can we do in \
some circumstances. There are cases when 3) 4) make sense and there are others where \
they don't. Here is an example when 3), 4) make sense:  @APIinterface ServiceProvider \
{    @Version("JDK7")    @ Deprecated // at JDK8     public String \
getServiceProperty(String property);  @Version("JDK8")    public Properties \
getAndParseConfigFile() default {        ...    }} @APIabstract class Service {
    ServiceProvider provider;    public Service(ServiceProvider p) { provider = p;}
        public String getProperty(String property) {                 // return \
                provider.getServiceProperty(property); this was at JDK7
        // this is the implementation at JDK8        return \
provider.getAndParseConfigFile().getProperty(property);     }    } @Clientpublic \
class SPImpl implements ServiceProvider {    @Override    public String \
getServiceProperty(String property) {...}} @Client class ServiceImpl extends Service \
{     } @Clientpublic class Application {
    Service service = new ServiceImpl(new SPImpl());
    public void m() { ... service.getProperty("property"); ... }}
As you can easily see from the above example, the API was extended with a virtual \
method at JDK8. However, that default implementation doesn't make any sense because \
there is no way to find the config file at API level (getAndParseConfigFile()) . So \
3) and 4) make sense, and can be considered as an option. In this case the best to do \
is throw UnsupportedOperationException. However, this example is so bad and of course \
no body could do this. But this is an alternative to take in consideration when \
dealing with virtual methods. Here is a case when 3) 4) doesn't make sense at all: \
consider we had this Service interface: @APIinterface ServiceProvider {    \
@Version("JDK7")    @ Deprecated // at JDK8     public String \
getServiceProperty(String property);  @Version("JDK7")    public File \
getConfigFile();  @Version("JDK8")    public Properties getAndParseConfigFile() \
default {        ...        File f = getConfigFile();        Properties p = new \
Properties();        p.load(new FileInputStream(file));        return p;    }} In \
this case 3) 4) doesn't make sense at all, because getAndParseConfigFile() can \
"reliably" provide a default implementation. So why throwing \
UnsupportedOperationException? It seems that I am in contradiction, but my intention \
was not to say that all 1) 2) 3) and so on, are true and they will be all supported, \
indeed I wanted just to point out some alternatives to take in consideration. To make \
it clear what keyword mean, just think of it as an operator that can control the \
visibility of virtual methods. As I previously stated in my emails, consider \
Collection interface. Someone has extended a List implementation and had some method \
like this: void MyList#map(), or int MyList#foreach(). Now int JDK8 the developers \
of, will add some method Collection#map(), with same signature but different return \
type. In this case the code of the client will break if this method will be visible \
to MyList. What should we do? Should we allow the client code to break or is better \
to accept the default implementation so when the runtime sees: Collection#foreach() \
it works with the virtual method and when it sees MyList#foreach() will work with \
this implementation or should it throw an UnsupportedOperationException? The keyword \
override in this case will help the client code in that that if it is not present it \
will not be visible to client code so it will not break, however in cases like the \
example above there are two alternatives: throw the exception or use the default \
implementation. Finally I don't think it is a good idea to introduce virtual methods \
only for the lambda project and to expect from developers that they will use them \
only in situations like Collections API, I think the best idea is to think of them as \
a general evolution of Java language that will impact all the fields where interfaces \
are used (all the programs :-)) and to deal with all the alternatives. As of your \
example: According to what I meant the use of public override void m() is valid. \
However, C will not compile because there is int m() which is in conflict (the return \
type is not compatible with). So will not work. The client who wrote override void \
m() is saying (in a simple logic)  that I will adopt the new API so I will refactor \
                my code to be compatible with, and I will do it on my own risk. 
Date: Thu, 15 Mar 2012 16:11:46 +0900
Subject: Re: Interface evolution via virtual extension methods - Extending Interfaces \
                By Breaking Application Logic (Dangerous)
From: bitterfoxc@gmail.com
To: elvis_ligu at hotmail.com
CC: lambda-dev at openjdk.java.net

2012/3/15 Elvis Ligu <elvis_ligu at hotmail.com>




Why? IImpl in JDK8 will not compile at all, because there are two m2() one return int \
and the other return void.I think you didn't got my point, or probably I didn't make \
it clear. However, private override void m2() in IImpl in 



JDK8 is invalid according to what I mean. You should have an implementation in IImpl \
if you write override..., unless IImpl is an abstract class or an interface (that is \
what I mean, or however what I wanted to say), and ofcourse it should 



be public override void m2(). The only one that will crash here is IImpl. 
Yes, I didn't get your point: "overrid"den method can have a implementation.




 When you extend an API with a virtual method you should provide a 



default implementation for it. So in your example if m2() is a virtual method it must \
have a default implementation.  Right. It is my easy mistake.
 


Because of this Processor#doProcess() will not break at all, neither the User#use(). \
                So there are to choices left to 
client: a) ignore I#m2() (in other words leave it as is, and accept the default \
implementation), b) provide your own implementation in IImpl which means THE CLIENT \
IS DOING ON HIS OWN, EITHER HE HAS TO REFACTOR HIS CODE 



TO RESOLVE CONFLICT BETWEEN int IImpl#m2() AND void I#m2() OR GO TO CHOICE a) AND \
IGNORE IT (HE STILL WILL BE ABLE TO USE JDK8), OR JUST DON'T USE JDK8 AT ALL AND BE \
HAPPY WITH JDK7.




To avoid misconception my intention was this: if you write override ... method() for \
a virtual method then it means you are going to give an implementation so it is \
visible to your class. If you have an interface with a virtual method but have 



no class implementation that overrides it then the virtual method will not be visible \
at all at this class BUT IT WILL BE RUNNING because there is a default implementation \
for it. So everyone is happy: API developers don't break their API, 



and client code continue to run.
I recognized by 3) or 4) that all of calling for "overrid"den method will be a cause \
of UnsupportedOperationException.

> 3) When a caller call Socket#reset(), if this method is ignored in the underlying \
> class implementation, in our case SecureRTPSocket, then a \
> UnsupportedOperationException will be thrown.



> This makes sense because, the old client code is ignoring Socket#reset() so it \
> simply means "I don't have any implementation for this".  4) When there exists a \
> void SecureRTPSocket#reset(). Again if the caller calls void Socket#reset() and the \
> void SecureRTPSocket#reset() exists before void Socket#reset() then an \
> UnsupportedOperationException is thrown.



> This makes sense because, the old client code will not have any call \
> Socket#reset(), instead it could have calls like SecureRTPSocket#reset() and this \
> will normally work because no resolution is required against Socket interface.




So I thought any implementation has no meaning.
But is the implementation for "overrid"den method valid as you told above?

class C implements I
{
    @Override
    public override void m()



    {
        System.out.println("A");
    }

    public int m()
    {
        System.out.println("B");

        return 0;
    }
}

C c = new C();
c.m(); // B



I i = c;
i.m(); // A

Is this result right?

Thank you for pointing out that I am in misunderstandings,
bitter_fox

 



Date: Thu, 15 Mar 2012 10:42:34 +0900
Subject: Re: FW: Interface evolution via virtual extension methods - Extending \
Interfaces By Breaking Application Logic (Dangerous)




From: bitterfoxc@gmail.com
To: elvis_ligu at hotmail.com
CC: lambda-dev at openjdk.java.net





I couldn't agree with you.
I don't think "override" resolve the problem.

For instance:

@API
@Version("JDK7")
interface I
{
    void m1();

}

@API
@Version("JDK7")

class Processor
{
    public void doProcess(I i)
    {
        i.m1();
    }
}

@Client
@Version("JDK7")
class IImpl implements I
{
    public void m1()
    {
        ...;





    }

    public int m2()
    {
        ...;
    }
}

@Client
@Version("JDK7")
class User
{
    public void use()
    {
        new Processor().doProcess(new IImpl());





    }
}

I think this is a very frequently pattern.

And in JDK8, @API Processor is updated with the update of I:

@API
@Version("JDK8")
interface I
{
    void m1();

    @Version("JDK8")





    void m2();
}

@API
@Version("JDK8")
class Processor
{
    public void doProcess(I i)
    {
        i.m1();
        i.m2();
    }
}

@Client
@Version("JDK8")





class IImpl implements I
{
    public void m1()
    {
        ...;
    }

    public int m2()
    {
        ...;
    }

    @Version("JDK8")
    private override void m2();





}

// User is retained at @Version("JDK7")

We can compile well every classes.
However User#use crashes in JDK8 at runtime.
So we find "override" just postpone occurring error until runtime.






And all of method invocations are in fear by "override".
Because it breaks the contract of types: a member declared in supertype is valid in a \
instance of every subtype. This is critical in Java that is strong typed language and \
a new problem.






I guess "override" might cause more trouble than it solves.

Regards,
bitter_fox


2012/3/14 Elvis Ligu <elvis_ligu at hotmail.com>











From: elvis_ligu@hotmail.com

To: brian.goetz at oracle.com

Subject: RE: Interface evolution via virtual extension methods - Extending Interfaces \
By Breaking Application Logic (Dangerous)

Date: Tue, 13 Mar 2012 16:40:14 +0100



























(I just saw my email, and I apologize for the horrible formatting)

I will try to give a very simple example in order to explain (at least I will give it \
a try) what I mean. Before starting let me tell you that I don't have a lot of \
knowledge about compilers and how things works into details, so I can't propose \
anything in detail. I am looking at the problem from a SE perspective and how to \
achieve a smooth transition from java 7 to java 8 avoiding the overall complexity and \
accidentally breaking the client code. By breaking client code I mean, breaking \
client's application even from not compiling at all (which is the least undesirable) \
or breaking client's application logic (the worst case is when the client's \
application compiles, run, and pass all tests but the application logic is in some \
way compromised).






Before continue this is how do I interpret the following concepts:

- Client code, API's client, and client in general -> the client is some other (let \
say a developer) different  from API. For example, I am a client of Collections API \
in Java SE 7, and my code is the client code. You  can assume my classes are API's \
client or I (as a developer) am the client. Generally speaking, the client  is \
independent from the API's development process, and is using the API according to its \
public contract  (the interface, whether they are classes or interfaces).






- Caller -> I would like to avoid the misunderstanding between an API's client and a \
class client. So when  I say caller I mean the client of a specific class (and or \
interface). I.e. in class A there is a statement   b.print(); in this case I would \
rather say class A (the instance of A) is the caller of class B (the instance of B). \
- Java Interface -> explicitly or implicitly a Java Interface (i.e, interface Service \
{}) is part of the  contract of the API. It doesn't provide any implementation (and \
it shouldn't), and the purpose of it  is to define a communication point between the \
implementation provider and the caller. An other purpose  is to define an implicit \
logic, i.e. Comparator has method compare(), where the method signature is   the \
definition of communication rules (the caller must call this method using its \
signature), and the logic   is that the method compare() takes two same arguments and \
return the order of those objects: 0 -> equals,  positive integer -> ob!






 j1 > obj2, negative integer -> obj1 < obj2. Of course there is nothing to prevent a \
client  to break the implicit logic an interface imposes, and this depends on \
implementation, however this is not an   issue from the API's developer perspective.






  Our example - the API:    interface Socket {       ...      // method added in \
release 1.1      public void consumePackets(final PacketBuffer<Packet> packets) \
default {                   final List<Packet> buffer = new ArrayList<Packet>();      \
synchronized(packets) {              while(packets.hasNext()) buffer.add(p.next());   \
}          final PacketSequence sequence = new PacketSequence(buffer.toArray(new \
Packet[0]));                   // an empty sequence will be returned if no previous \
one exists         final PacketSequence current = \
getPreviousSequence().merge(sequence);         setCurrentSequence(current);      }    \
...   }   interface SecureSocket extends Socket { }    interface RTPSocket {       \
...      // this is defined in release 2.0      public void consumePackets(final \
PacketBuffer<Packet> packets) default {                     final List<Packet> buffer \
= new ArrayList<Packet>();          synchronized(packets) {              \
while(packets.hasN!






 ext()) {                  Packet p = p.next();                 buffer.add(p);        \
// send each packet to handler to avoid delay                 \
transmitPacketsToRTPHandlers(new Packet[]{p});         }          final \
PacketSequence sequence = new PacketSequence(buffer.toArray(new Packet[0]));






         // an empty sequence will be returned if no previous one exists         \
final PacketSequence current = getPreviousSequence().merge(sequence);         \
setCurrentSequence(current);      }      ...  }      class DefaultSocket implements \
Socket{}  class DefaultSecureSocket extends DefaultSocket implements SecureSocket{}  \
class DefaultRTPSocket extends DefaultSocket implemenets RTPSocket{}






Here the API designers, have designed an API which provide socket communication to \
its clients. In release 1.0 the Socket interface didn't have a consumePackets() \
method. In release 1.1 API designers would like to extend Socket interface so they \
introduced consumePackets() and provided a default implementation because they don't \
want to break the clients' code. In release 2.0 they decided to change the default \
implementation of consumePackets() because they observed their DefaultRTPSocket was \
having delays when this method was called. They decided to deliver each packet to the \
handlers while consuming the packets and leave the rest as is.






Our example - API's client

  // defined after release 1.1 of API  class SecureRTPSocket extends \
DefaultSecureSocket implements RTPSocket {      ...      public void \
setCurrentSequence(PackeSequence sequence) {           \
doubleBufferSequence(sequence);           \
transmitPacketsToRTPHandlers(getPacketsArray());       }






      // this is not override, this is defined here      public int reset() { ... };  \
}

Here the API's client, using the version 1.1 decided to construct a new secure rtp \
socket. He used the default implementation of consumePackets() because it did meet \
his requirements. Because he already observed the delay produced by consumePackets() \
he decided to use a double buffer in order to avoid it. His application was working \
perfectly until he decided to replace the old API v1.1 with the new API v2.0. After \
replacing the API its application started to have delays! In this case the delay \
comes from the fact that API designers override the default implementation of \
consumePackets(), and the handler is getting duplicate packets!!! Of course the \
handler is smart enough to reject duplicate packets. But the problem is still there, \
so the client is going to invest it. The client is lookingat DefaultSecureSocket and \
at Socket#consumePackets() but he can't find the problem. Why he is not looking at \
RTPSocket? Well for 15 years now Java developers don't expect the interface to!






  impact their implementation without breaking the contract (look at the definition \
above). I know such a powerful mechanism in Java would by very great and using it \
wisely would provide so much flexibility to an API developer. On the other hand it \
increases the possibility of writing fragile code. Of course the above example is \
such a bad one and can be easily avoided, but again virtual methods are increasing \
risks. Think of C++ and its power, think of multiply inheritance, one can say that it \
is powerful, however (in my opinion) it complicates things and can lead at more \
fragile code. As you stated this is an old problem, because this problem is still \
present with abstract classes for example, but as I previously stated a Java \
developer has never looked at Interfaces as they are abstract classes. Should they \
start doing it now?






As I can see you are working at Oracle, and I understand that when it comes to make a \
change to existing Java API it is a very critical decision, to be or not to be. One \
of the big problems you should face with is backward compatibility. I was reading \
some email from a guy here at this mail list where he was concerned about Collections \
API and the changes that can possibly be made at Java 8. He had extended a \
Collections interface and added some methods, let say foreach(). In this case if you \
add a method foreach() it would possibly break his API and this is not what you want. \
In the above example if the client had a method int reset() in SecureRTPSocket the \
API designers will not be able to add a method void reset() in Socket interface, at \
least not without breaking the client's code. The problem here is that API developers \
don't know how API clients are using their API so they can not make any assumption. \
Actually the virtual methods gives you the (virtual reality) sense o!






 f thinking that by adding a method to an interface you, will not break the API, but \
as you we can easily see this is not the case.

* Here I am not criticizing the virtual methods, I like the idea very much. I am \
rising some of the facts we need to consider when dealing with them.

However, I think there is an interesting solution we can avoid such problems, \
especially API breaks (from the perspective of API designer). Looking at the above \
example, let consider the API designers would like to add a method void reset() at \
Socket interface. In this case we could have a client's code break here, because \
there is a int reset() method. To avoid this, we can have an other keyword: \
override.In order the SecureRTPSocket to see the new virtual method he has to write \
something like this:






 private override void reset(){...}

If the client doesn't write something like this than this method is not visible to \
client's class SecureRTPSocket. Some one would say this is not possible because what \
if some one makes a call like this:

 Socket socket = getSocket(); socket.reset();

In such a case if getSocket() has a return type Socket and the implementation of \
getSocket() returns a SecureRTPSocketthere are two possibilities: 1) method reset() \
is already present in SecureRTPSocket, 2) method reset() is not present. Here how it \
can possibly works:






1) In version 1.1 the client didn't know any Socket#reset() method so there is not a \
statement in his code that will call Socket#reset().If the Socket#reset() method is \
ignored in v2.0 his code will continue to be compatible even in case there is a int \
SecureRTPSocket#reset().






2) There is a void SecureRTPSocket#reset() method. Even in this case the \
Socket#reset() method will be ignored if called as above. At least in client's code \
will not be any Socket#reset() call, because his code was written based on v1.1.






3) When a caller call Socket#reset(), if this method is ignored in the underlying \
class implementation, in our case SecureRTPSocket, then a \
UnsupportedOperationException will be thrown. This makes sense because, the old \
client code is ignoring Socket#reset() so it simply means "I don't have any \
implementation for this".






4) When there exists a void SecureRTPSocket#reset(). Again if the caller calls void \
Socket#reset() and the void SecureRTPSocket#reset() existsbefore void Socket#reset() \
then an UnsupportedOperationException is thrown. This makes sense because, the old \
client code will not have any call Socket#reset(), instead it could have calls like \
SecureRTPSocket#reset() and this will normally work because no resolution is required \
against Socket interface.






5) When there exists a override void DefaultSocket#reset(). Well in this case the \
void Socket#reset() will be visible to DefaultSocket, and it will be visible to \
DefaultSecureSocket and DefaultRTPSocket, and so it will be visible to \
SecureRTPSocket, because SecureRTPSocket extends DefaultSecureSocket. If there exist \
void SecureRTPSocket#reset() then it just overrides void DefaultSecureSocket#reset() \
so there is not a problem in this case. If there exist a int SecureRTPSocket#reset() \
than client's code will break (will not compile at all). This makes sense because \
when a client extends API's classes it means he knows what he is doing and he \
understand the internals of those classes, so he is aware of any change. In our \
example a good developer would simply uses encapsulation, and have something like \
this: class SecureRTSocket implements SecureSocket, RTSocket, and uses a private \
DefaultSecureSocket to delegate some of its methods and implements the rest. However, \
if this is n!






 ot a good approach an other alternative would be to ignore the virtual methods just \
like in the above cases. The last approach introduces an extra effort because it \
requires from API developers to write override void reset() to each implementation \
class (Default...Socket) but at least this ensures 100% backward compatibility. Think \
of void Collection#foreach, it will require for each Collection's subtype to write \
override void foreach(). Is this affordable? Well, the virtual methods are used to \
extends an API so I think this is an effort that it worth.






6) What does override void reset() implementation body contains? The case is: a) it \
can completely override the super's default implementation, b) it can write something \
like this SuperType.reset() in order to accept super's default implementation.






7) Where the client has already extended Socket interface, let say he has an \
interface like this ISecureRTPSocket and SecureRTPSocket class implements this \
interface. In this case void Socket#reset() is a virtual method so it is visible to \
ISecureRTPSocket as a virtual one. But it is not visible to SecureRTPSocket because \
the doesn't exists any override void SecureRTPSocket#reset(). Here we don't have any \
client's code compatibility issues, it works just like the above cases.






8) Where there doesn't exists any override void SecureRTPSocket#reset() (look at the \
example above). In this case if the first approach will be implemented then if exists \
override void DefaultSocket#reset() the reset() method will be visible to all other \
subclasses just like any other method. If the second approach will be implemented \
then, if there doesn't exist any override void DefaultSocket#reset() then it will not \
be visible to DefaultSecureSocket so a override void DefaultSecureSocket#reset() will \
be a compilation error (not in our case because DefaultSecureSocket implements \
SecureSocket, reset() is visible to SecureSocket and so override void \
DefaultSecureSocket#reset() can be overrided). The rule is simple: if we have \
interface A, B and C extends A, then virtual method A#m() will be visible to B and C. \
If classes Bimp implements B, Dimp extends Bimp, if there exists override Bimp#m() \
then could exist override Dimp#m() too, if not then Dimp must explicitly implements !






 A, B or C so we can have override Dimp#m().

9) The compiler and runtime will be crazy with all this complexity. Actually I can't \
give any reliable response to this. From a higher level I think the compiler and \
runtime can treat virtual methods just like any other method but with some small \
differences. In case there is a Interface to Interface resolution they can easily be \
treated just like other methods, after all we can keep backward compatibility because \
an interface is not required to have an implementation so the virtual method in my \
interfaces will not break other interfaces. In case of classes the compiler and or \
runtime can make the distinct between virtual methods and other regular methods by \
keeping (let say) an extra bit for a method. So when this bit is 0 it is a regular \
method and the resolution is the same as before in Java 7, when this bit is 1 it is a \
virtual method so the resolution can follow the rules I stated above. Especially if \
the second approach will be implemented (see 5) above) the compiler c!






 an easily keep track of virtual methods because the keyword override will be present \
in any class that implements the virtual method so it can deal with their (strange) \
resolutions and conflicts.

Conclusions:

I think the approach of keyword override and the resolution of virtual methods in \
this way doesn't break any OOP principles (well I am just an undergraduate student \
that is going to be graduated soon, so I cant say this for sure :-)). Indeed it \
imposes good OOP because it requires the attention of programmer. Think of this \
scenario: I am about to work at a company and my boss just gave me a job description \
(the interface, which is the contract). I take my responsibilities and try to do my \
best to do my job according to its description. My boss has a new job description for \
me and he required to meet me to announce that. In the meanwhile some guy from staff \
who already knows that my by boss is having a new job description for me comes to me \
and requires me to do some job that is in the new job description not in the one I \
have. What should I do in this case? Should I try to do what he says, at the risk of \
doing something that I am not prepare to do, or should I say to him Uns!






 upportedJobException? When I get my new job description from my boss I sit down and \
learn how to do it and write to my notes override JobDesc#newJob1(), override \
JobDesc#newJob2() and so on. Next time the guy from staff comes to me I would be \
prepared and happily do what he requires by taking my responsibilities.






Thank you for hearing me. I know there are brilliant minds out there that could make \
all of my ideas useless, unfortunately I am not an experienced Software Engineer \
(well actually not even a new SE :-)) so I could not think of all the tricks. At \
least I tried to squeeze my little mind and express my thoughts (damn English I \
really want to master you :-).






I am looking forward to hearing from you.



> Subject: Re: Interface evolution via virtual extension methods - Extending \
> Interfaces By Breaking Application Logic (Dangerous)

> From: brian.goetz at oracle.com

> Date: Sun, 11 Mar 2012 21:51:35 -0700

> CC: lambda-dev at openjdk.java.net

> To: elvis_ligu at hotmail.com

> 

> Thanks for your comments.  Responses inline:

> 

> > 1 - There is a risk of breaking client code: consider a developer who has already \
> > implemented a comparator like this:          abstract class MyComparator \
> > implements Comparator { protected int order = 1; public void reverseOrder() { \
> > return -1*order;} }






> > and uses this somewhere in his code like this:          MyComparator c = new \
> > MyComparator() { public int compare(Objct o1, Object o2) { return order* \
> > (o1.toString().compareTo(o2.toString())); }}     .... c.reverseOrder();     \
> > Arrays.sort(list, c);        If oracle developers decide to add such a method in \
> > Comparator interface then they can not ensure that client code will not break; \
> > two methods     with the same signature but with a different returned type can \
> > not compile (at least not in java 7).






> 

> Yes, this is a risk.  Note that this is not new with extension methods; this risk \
> exists for adding new methods to existing classes (abstract or concrete) too.

> 

> The alternative is: never add any new methods to libraries.  We find this \
> alternative unappealing!

> 

> > 2 - There is a risk of breaking client logic: according to Brian's paper, section \
> > 3. Method Dispatch, we can have a diamond-shaped hierarchy as:

> > interface A { void m() default X.a; }     interface B extends A { }     interface \
> > C extends A { }     class D implements B, C { }

> > When the compiler sees d.m() and if there is not an implementation of m() in \
> > class D then it will call the default method implementation A.m().

> 

> The compiler does *not* short-circuit calls to D.m() to A.m().  The compiler emits \
> an invokespecial with D.m(); the runtime will perform the resolution, and if the \
> situation is still like this by the time the classes are actually loaded, then the \
> VM will decide to dispatch to A.m().  (This is no different than today; the \
> compiler's job is simply to sanity-check linkage and "fail fast" if the compiler \
> can see the call would not succeed if made at runtime with the classes as they are \
> at compile time, but the runtime builds vtables based on the classes it sees at \
> runtime.)






> 

> > The client of this API can easily "make the mistake" of relying on default \
> > implementation of A.m().

> 

> Again, this is nothing new.  Let's say class C has a method q(), and D extends C \
> but does not override q().  Clients of D could easily "make the mistake" of \
> assuming calls to D.q() are going to end up at C.q(), but that is simply a mistake.






> 

> > For some reason in the future the developerof the API would like to add a default \
> > implementation of m() in B. The problem in this case is bigger than in case 1;

> > a) if the client of API have already extended interface A with B1 and provided a \
> > default implementation for m, and has a "class E implements B, B1" the client \
> > code will break.

> 

> At this point E will not compile, due to competing implementations in B and B1.

> 

> > b) the default m() in B will probably break invariants of a "class MyClass \
> > implements B, C" and cause no compile error.

> 

> Why?  If B.m() overrides A.m(), we expect B.m() to implement the contract of A.m(). \
> Note that interfaces have no state (barring heroic actions), so the protection of \
> state-based invariants still falls entirely to C and its subclasses.  The \
> implementation of m() in A or B can only call other interface methods, and if any \
> of those affect state in C, eventually we'll be calling code in C to actually touch \
> the state.






> 

> > 1 - How to keep backward compatibility? Well at least in the transition phase \
> > from java 7 to java 8 nothing can ensure the java developers that adding a \
> > virtual method inCollection interface will be compatible with the old java. So my \
> > suggestion is another keyword, let say override. Let say we add reverseOrder() in \
> > interface Comparator.In this case the java compiler will make the reverseOrder() \
> > visible to Comparator's subtypes only and only if the subtype explicitly define: \
> > override reverseOrder().






> 

> I believe this is a request for "opting in" to extension methods, rather than \
> having them actually inherited?  I invite you to propose exactly how this might \
> work in more detail.

> 

> > So incase 1 MyComparator (the client code) is not affected; reverseOrder() is not \
> > visible in MyComparator.  2 - How to avoid making API fragile by default (case \
> > 2)? My suggestion is the same as the above: the keyword override. By writing \
> > override reverseOrder() the API's clientis required to explicitly define which \
> > default implementation he is relying on, specifying SuperType.super.m() or make a \
> > "default none" in case of an interface. If the clientdoesn't specify override, \
> > there i!






> 

> You say "client" when I think you mean "subclass".  Are you really suggesting the \
> opt-in should be at the *client* site, or are you speaking only at the inheritance \
> site?

> 










 		 	   		  

 		 	   		  


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic