13

In English, a homograph pair is two words that have the same spelling but different meanings.

In software engineering, a pair of homographic methods is two methods with the same name but different requirements. Let's see a contrived example to make the question as clear as possible:

interface I1 { 
    /** return 1 */ 
    int f()
}
interface I2 {
    /** return 2*/
    int f()
}
interface I12 extends I1, I2 {}

How can I implement I12? C# has a way to do this, but Java doesn't. So the only way around is a hack. How can it be done with reflection/bytecode tricks/etc most reliably (i.e it doesn't have to be a perfect solution, I just want the one that works the best)?


Note that some existing closed source massive piece of legacy code which I cannot legally reverse engineer requires a parameter of type I12 and delegates the I12 both to code that has I1 as a parameter, and code that has I2 as a parameter. So basically I need to make an instance of I12 that knows when it should act as I1 and when it should act as I2, which I believe can be done by looking at the bytecode at runtime of the immediate caller. We can assume that no reflection is used by the callers, because this is straightforward code. The problem is that the author of I12 didn't expect that Java merges f from both interfaces, so now I have to come up with the best hack around the problem. Nothing calls I12.f (obviously if the author wrote some code that actually calls I12.f, he would have noticed the problem before selling it).

Note that I'm actually looking for an answer to this question, not how to restructure the code that I can't change. I'm looking for the best heuristic possible or an exact solution if one exists. See Gray's answer for a valid example (I'm sure there are more robust solutions).


Here is a concrete example of how the problem of homographic methods within two interfaces can happen. And here is another concrete example:

I have the following 6 simple classes/interfaces. It resembles a business around a theater and the artists who perform in it. For simplicity and to be specific, let's assume they are all created by different people.

Set represents a set, as in set theory:

interface Set {
    /** Complements this set,
        i.e: all elements in the set are removed,
        and all other elements in the universe are added. */
    public void complement();
    /** Remove an arbitrary element from the set */
    public void remove();
    public boolean empty();
}

HRDepartment uses Set to represent employees. It uses a sophisticated process to decode which employees to hire/fire:

import java.util.Random;
class HRDepartment {
    private Random random = new Random();
    private Set employees;

    public HRDepartment(Set employees) {
        this.employees = employees;
    }

    public void doHiringAndLayingoffProcess() {
        if (random.nextBoolean())
            employees.complement();
        else
            employees.remove();
        if (employees.empty())
            employees.complement();
    }
}

The universe of a Set of employees would probably be the employees who have applied to the employer. So when complement is called on that set, all the existing employees are fired, and all the other ones that applied previously are hired.

Artist represents an artist, such as a musician or an actor. An artist has an ego. This ego can increase when others compliment him:

interface Artist {
    /** Complements the artist. Increases ego. */
    public void complement();
    public int getEgo();
}

Theater makes an Artist perform, which possibly causes the Artist to be complemented. The theater's audience can judge the artist between performances. The higher the ego of the performer, the more likely the audience will like the Artist, but if the ego goes beyond a certain point, the artist will be viewed negatively by the audience:

import java.util.Random;
public class Theater {
    private Artist artist;
    private Random random = new Random();

    public Theater(Artist artist) {
        this.artist = artist;
    }
    public void perform() {
        if (random.nextBoolean())
            artist.complement();
    }
    public boolean judge() {
        int ego = artist.getEgo();
        if (ego > 10)
            return false;
        return (ego - random.nextInt(15) > 0);
    }
}

ArtistSet is simply an Artist and a Set:

/** A set of associated artists, e.g: a band. */
interface ArtistSet extends Set, Artist {
}

TheaterManager runs the show. If the theater's audience judges the artist negatively, the theater talks to the HR department, which will in turn fire artists, hire new ones, etc:

class TheaterManager {
    private Theater theater;
    private HRDepartment hr;

    public TheaterManager(ArtistSet artists) {
        this.theater = new Theater(artists);
        this.hr = new HRDepartment(artists);
    }

    public void runShow() {
        theater.perform();
        if (!theater.judge()) {
            hr.doHiringAndLayingoffProcess();
        }
    }
}

The problem becomes clear once you try to implement an ArtistSet: both superinterfaces specify that complement should do something else, so you have to implement two complement methods with the same signature within the same class, somehow. Artist.complement is a homograph of Set.complement.

Community
  • 1
  • 1
Dog
  • 7,707
  • 8
  • 40
  • 74
  • 1
    Possible duplicate of http://stackoverflow.com/questions/2801878/implemeting-2-interfaces-in-a-class-with-same-method-which-interface-method-is-o – Harshal Pandya May 11 '13 at 06:20
  • 1
    @Dog should ArtistSet implement Artist.. ArtistSet sounds to be just a collection of artists.. it's not an Artist in it's own. – Dev Blanked May 11 '13 at 06:26
  • @DevBlanked: for example, an `ArtistSet` implementation could have the ego of the whole Artist being a sum of egos of each artist in the set. – Dog May 11 '13 at 06:32
  • @Dog sounds like you need a different 'HasEgo' interface which the Artist and ArtistSet can separately implement.. – Dev Blanked May 11 '13 at 06:36
  • @HarshalPandya: This question is more specific: two methods are homographs, but they have *different requirements*. The question you linked to doesn't focus on addressing this issue. – Dog May 11 '13 at 06:36
  • @DevBlanked: I'm not looking to work around this issue by telling my 6 vendors to change their code. – Dog May 11 '13 at 06:37
  • @Dog so you want the complement method of ArtistSet to behave one way when called by theater and in a different way when called by hr department is it ? – Dev Blanked May 11 '13 at 06:51
  • 3
    It's too bad that the `Artist` interface's method was the wrong `complement`; it's actually misspelled. The method for `Artist` should have been `compliment()`, with an "i". The meanings of the two `complement` methods actually correspond to the English _homophones_ "complement" (for `Set`) and "compliment" (for `Artist`). – rgettman May 13 '13 at 19:37
  • @DevBlanked: If Java doesn't have a way to implement `complement` for both superinterfaces like the way C# does, yes. – Dog May 13 '13 at 20:01
  • @rgettman: oops. let's pretend they are spelled the same. – Dog May 13 '13 at 20:02
  • Is this some kind of homework assignment? Or a simplification of a real world problem you are trying to solve? – Perception May 14 '13 at 11:45
  • @Perception: The problem is fully described in the first paragraph and again in the last. The example is merely a concrete example of the problem. Here is where I discovered the problem (when I translated it from a problem in my real code): http://stackoverflow.com/q/15795058/2213023 – Dog May 14 '13 at 15:34
  • @Dog: Is part of the problem that one vendor owns `Artist` while another owns `Set`? `ArtistSet` seems to be dependent on both `Artist` and `Set`, so, who owns it - you? Which classes can you touch/modify and which are unchangeable? (**EDITED** - changed owned to dependent) – Andrew Alcock May 16 '13 at 01:22
  • @AndrewAlcock, each class is owned by another vendor. You can cut it down to 3 vendors if you want (A owns `Set` and `HRDepartment`, B owns `Artist` and `Theater`, C owns `ArtistSet` and `TheaterManager`), it doesn't change the problem. I am the one who has to implement `ArtistSet` to pass it to `TheaterManager`, and it seems the only way to do this is reflection and other tricks, so I want to find the most reliable way to do this. – Dog May 16 '13 at 19:53
  • 1
    The problem as described cannot be solved. Either the problem is [too hypothetical](http://stackoverflow.com/a/16621198/712765) or [the description is wrong](http://stackoverflow.com/a/16627961/712765). – Old Pro May 19 '13 at 04:36
  • @OldPro: The problem is very simple and is described in the first paragraph. Just because there is might not be no 100% good solution doesn't mean the problem is invalid. See e.g Gray's answer. I'm not even sure you understand the problem, you just keep trying to find reasons why you think the question is invalid. Even if this example is invalid, I can come up with 100 more examples, which you will probably claim are invalid for other reasons. – Dog May 19 '13 at 14:54
  • @OldPro: The problem is **very simple**: Some vendor made code that requires a **class to implement two interfaces**, each of which have a **method with the same name and same signature**. **Whether this is the vendor's fault is irrelevant.** The problem is that I have some code like this, and I need to work around it in the most reliable way. See again, Gray's answer, which provides an approximation of a reliable workaround for this simple problem. – Dog May 19 '13 at 14:58
  • @Dog, I've given you **two** answers, one showing an **example of how it is impossible to know what to do** in one case, even with a magical compiler, and no attempt at a solution for this case in Gray's answer, and one showing a **practical solution** easier to implement than Gray's that handles all real-word cases. Reread those answers (which I have improved since originally posting them) and explain what ArtstSet.complement() should do when called by TalentAgent.pr() and why Sybil is not adequate to your **actual** programming problem. SO is **NOT** a place for theoretical discussions. – Old Pro May 19 '13 at 18:00
  • @OldPro: This is not a "theoretical discussion", this is a simple problem explained in the first paragraph. I'm not going to rephrase the question again, you simply don't understand it and keep making strawman arguments wherein you still fail to even defeat the strawman. I will now disregard any further comments you make. Please stop spamming my questions with your answers unless you want to address the actual question. If it's not possible to solve, just say so, and explain why this can't even be done with bytecode manipulation (I suspect it can). – Dog May 19 '13 at 21:29
  • @Dog _"If it's not possible to solve, just say so, and explain why this can't even be done with bytecode manipulation."_ I did say so and I did explain why. You have not said what is wrong with my explanation or my practical solution to the problem you presented. All you have said is that my explanation as to why this cannot be solved in the general case does not solve your practical problem. Your first paragraph does not explain a practical problem, it asks a theoretical one. Sybil solves the problem you linked to in the first paragraph. – Old Pro May 19 '13 at 21:47
  • @OldPro: C# specifically has a construct to solve this problem (C# is used by millions of people), Java doesn't. There is nothing "theoretical" or "impractical" about it. The problem arise *in practice* when someone thinks Java works the same way C# does in this aspect, and ends up creating a pathological interface, then I get stuck with legacy code with such an interface. Nobody tests interfaces, so they will never find the bug until a customer hits it. Come think about it, pretty much anyone coming from C# will probably get hit by this problem if they come across an opportunity to be hit. – Dog May 19 '13 at 21:58
  • There's a way to get the bytecode at runtime under the default classloader (which is fine because I can choose the VM/environment): http://stackoverflow.com/questions/2737285/is-there-a-way-to-obtain-the-bytecode-for-a-class-at-runtime Now we just need to find which interface is being invoked by looking at the bytecode of the immediate caller on the stack. I'm feeling kind of lazy right now but I might give it a try later this week if nobody else answers. – Dog May 19 '13 at 22:23
  • @SargeBorsch: Will it let me implement an existing compiled Java interface with conflicting superinterfaces? – Dog May 19 '13 at 23:23
  • @Dog, Thank you for clarifying your question. I have clarified my answer. The best solution is to replace TheaterManager with a class that doesn't have this problem. If you can't write one yourself, you could perhaps create an effective solution with proxies. It is very dependent on the intricate details of your situation. – Old Pro May 20 '13 at 02:48
  • @OldPro: `TheaterManager` is an example, the real code isn't trivial, and I can't reverse engineer it. – Dog May 20 '13 at 13:10
  • @OldPro You seem to be missing the point that the question is looking for a generic solution (hack) to all instances of this problem (as given in Gray's answer), if you want to argue against a concrete example then go argue here: http://stackoverflow.com/questions/15795058/triads-not-showing-up-to-fight-java-set-missing-an-item – Dog May 20 '13 at 13:19
  • @Dog, I have explained [here](http://stackoverflow.com/a/16640841/712765) why there is no generic solution to all instances of the problem. You are going around in circles: If I give you a solution to an actual problem, you complain that it's not generic enough, but when I explain there is no solution to the generic problem, you complain that a generic solution is not required because you have a practical one to solve. The "triads" question you linked to is solvable because there the library taking `I12` always expects the `I2` behavior so you can just implement `I12` that way. – Old Pro May 20 '13 at 17:37
  • @OldPro: I have a practical problem to solve but can't post it due to NDA etc, so I ask a generic version instead. The question was clear from the get go, at first it was "is there a java construct for this?", which turned out the answer is no, so the only remaining way is by hack. The problem is how to do this generically. If you look at all the edits you will see that the question has never changed, you just keep giving ad-hoc unrelated answers so I have to explain why they're all wrong, because you make the problem concrete and argue about your concrete version you made up. – Dog May 20 '13 at 18:07
  • I have this solution in mind: Look at the caller on the stack, look at its bytecode, there should be a reference of the specific interface its trying to use, since bytecode is typed. I just realized readers may be thinking of calling `compareTo` on `I12`, but that is obviously a bug and nobody will ever do that. The problem is that an `I12` gets delegated as an `I1` and an `I2`. – Dog May 20 '13 at 18:12
  • 3
    Find bug in java language definition. Ask how to work around it. Thread spammed by fanboys claiming bug doesn't matter. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Jun 29 '13 at 19:31
  • @L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Couldn't agree more. – asteri Jul 18 '13 at 19:22

7 Answers7

4

New idea, kinda messy...

public class MyArtistSet implements ArtistSet {

    public void complement() {
        StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();

        // the last element in stackTraceElements is the least recent method invocation
        // so we want the one near the top, probably index 1, but you might have to play
        // with it to figure it out: could do something like this

        boolean callCameFromHR = false;
        boolean callCameFromTheatre = false;

        for(int i = 0; i < 3; i++) {
           if(stackTraceElements[i].getClassName().contains("Theatre")) {
               callCameFromTheatre = true;
           }
           if(stackTraceElements[i].getClassName().contains("HRDepartment")) {
               callCameFromHR = true;
           }
        }

        if(callCameFromHR && callCameFromTheatre) {
            // problem
        }
        else if(callCameFromHR) {
            // respond one way
        }
        else if(callCameFromTheatre) {
            // respond another way
        }
        else {
            // it didn't come from either
        }
    }
}
Gray Kemmey
  • 976
  • 8
  • 12
  • Notice that I said each of those 6 classes are written by different people. I can't change any of them. I only can implement `ArtistSet`, and pass it to `TheaterManager`. – Dog May 11 '13 at 06:38
  • Updated answer with different approach. – Gray Kemmey May 11 '13 at 07:21
  • draw back of this approach is ArtistSet needs to know about everybody else who is going to call the 'complement' method. But i also see no other alternative ... – Dev Blanked May 11 '13 at 07:25
  • Unless that final `else` could be some default implementation for everybody else. But, I admit, it's ugly as sin. – Gray Kemmey May 11 '13 at 07:26
  • You can always hack the language to accomplish something that wasn't meant to be accomplished, but is this really helping the OP's purpose? Would any sane developer use that in his product? – SirKnigget May 16 '13 at 15:47
  • 1
    @SirKnigget: This is the only answer so far that actually answers the question. Vendors put me in this situation and now I have to work around it in the most robust way possible. I think there might be a more reliable way than this though. – Dog May 16 '13 at 16:18
  • @SirKnigget, I don't know of a better way to do it. Doesn't look like you do either. The OP is asking for a way around a language feature of Java--explain to me how that's not going to result in a hack. – Gray Kemmey May 16 '13 at 16:53
  • The answer by Glen Best seem to address the issue's core - the design itself and the understanding of OOP. I didn't say that your answer doesn't technically work - I am saying that it's not suitable for any releasable and maintainable product. – SirKnigget May 16 '13 at 17:03
  • 1
    I'm aware of a Composite Object, but that doesn't allow for responding to the `complement` method one way if it comes from a `Theatre` and another way if it comes from an `HRDepartment`, and that seems to be what the OP is asking for. – Gray Kemmey May 16 '13 at 17:10
  • A valiant attempt!! But you need the calling class to contain the string "Theatre" or "HDRDepartment" in it's name? That's beyond "kinda messy". That's a bomb in place. You couldn't seriously put that into code and deliver to a customer. – Glen Best May 20 '13 at 09:26
  • 5
    Luckily, he said the vendor code (i.e. the code that makes sure the calling classes have those Strings), can not be changed. Look, I have admitted, more than once, that it's a mess, but the OP asked for a way to circumvent a feature of Java. So yeah, I gave him a hack/messy/dangerous solution, but at least it's actually a solution. So let's stop pontificating about why my answer isn't right. Everyone, including me, is aware of the drawbacks. – Gray Kemmey May 20 '13 at 18:39
  • @GlenBest: To make this **blantantly clear** (since nobody seems to understand it): As Gray said, this answer answers the question (but there may be better hacks). – Dog May 21 '13 at 20:14
3

How to Solve For Your Specific Case

ArtistSet is simply an Artist and a Set:

 /** A set of associated artists, e.g: a band. */
 interface ArtistSet extends Set, Artist { }

From an OO perspective, that's not a useful declaration. An Artist is a type of noun, a "thing" that has defined properties and actions (methods). A Set is an aggregate of things - a collection of unique elements. Instead, try:

ArtistSet is simply a Set of Artists.

 /** A set of associated artists, e.g: a band. */
 interface ArtistSet extends Set<Artist> { };

Then, for your particular case, the homonym methods are on interfaces that are never combined within the one type, so you have no clash and can program away...

Further, you don't need to declare ArtistSet because you aren't actually extending Set with any new declarations. You're just instantiating a type parameter, so you can replace all usage with Set<Artist>.

How to Solve For the More General Case

For this clash the method names don't even need to be homographic in the english language sense - they can be the same word with same english meaning, used in different contexts in java. Clash occurs if you have two interfaces that you wish to apply to a type but they contain the same declaration (e.g. method signature) with conflicting semantic/processing definitions.

Java does not allow you to implement the behaviour you request - you must have an alternative work-around. Java doesn't allow a class to provide multiple implementations for the same method signature from multiple different interfaces (implementing the same method multiple times with some form of qualification/alias/annotation to distinguish). See Java overriding two interfaces, clash of method names, Java - Method name collision in interface implementation

E.g. If you have the following

 interface TV {
     void switchOn();
     void switchOff();
     void changeChannel(int ChannelNumber);
 }

 interface Video {
     void switchOn();
     void switchOff();
     void eject();
     void play();
     void stop();
 }

Then if you have an object that is both of these things, you can combine the two in a new interface (optional) or type:

interface TVVideo {
     TV getTv();
     Video getVideo();
}


class TVVideoImpl implements TVVideo {
     TV tv;
     Video video;

     public TVVideoImpl() {
         tv = new SomeTVImpl(....);
         video = new SomeVideoImpl(....);
     }

     TV getTv() { return tv };
     Video getVideo() { return video };
}
Community
  • 1
  • 1
Glen Best
  • 22,769
  • 3
  • 58
  • 74
  • Are you saying you shouldn't use multiple interfaces in Java? – Dog May 16 '13 at 16:52
  • No. Should be clearer here - using multiple interfaces is a good thing if the class has multiple behaviours that can be used independently by other classes. Usually class composition is useful as a tool to aggregate muliple behaviours from other classes. Here, interface composition is useful to avoid the method signature clash problem in the Q. – Glen Best May 17 '13 at 02:50
  • The problem is that the vendor who wrote `ArtistSet` didn't notice that the two superinterfaces clash, so I need to find the most reliable workaround. Are you saying the vendor is doing Java wrong because he doesn't check every superinterface/superclass for clashes every time he makes an interface/class? – Dog May 17 '13 at 15:04
  • "ArtistSet" implenting Artist & Set makes no sense - see first part of A. If the two interfaces from vendors are independent e.g. TV and Video, you can create your own composition interface & composition class & coordinate calls to separate vendor classes. If vendor interfaces & classes are inter-dependent then you *must* follow the vendor's pre-integrated design. Cheers :) – Glen Best May 18 '13 at 00:18
  • What do you think is a valid use case for multiple superinterfaces? Even if the `ArtistSet` example is bad design, it still is just an example of homographic methods, and the question is "how to implement interfaces with homographic methods", not "is there anything wrong with this example code". – Dog May 19 '13 at 14:41
  • A valid use case: one where there's a hierarchical specialisation/generalisation relationship and different code clients might use a different level of detail/specialisation in the hierarchy e.g java.util.SortedSet extends java.util.Set extends java.util.Collection extends java.lang.iterable. My A answers your Q: a java class can't provide multiple implementations for the same method signature from multiple interfaces, so must avoid the situation. As for "is there anything wrong with this example code" - it's relevant to point out major logical/modelling issues when advising best practice. – Glen Best May 20 '13 at 01:30
  • You say there exists a valid use case for multiple interfaces. Then obviously it's possible for an author to accidentally introduce 2 superinterfaces with homographic methods. The question is how to workaround code that makes this mistake. – Dog May 21 '13 at 20:18
  • Of course. Can have multiple interfaces with identical method signatures. But if you extend an interface and introduce an identical method signature with contradictory behaviour, then that's a method override. A class implementation can only implement one such definition. While there are valid use cases (wish extension interfaces/classes to reuse *some* code from "base" set of interfaces/classes) it's dangerous & generally best avoided (can't reuse *all* code from "base" set of interfaces/classes). Violates "L" in SOLID (Liskov Subtitution Principle). But code still works fine with care. – Glen Best May 22 '13 at 01:00
3

Despite Gray Kemmey's valiant attempt, I would say the problem as you have stated it is not solvable. As a general rule given an ArtistSet you cannot know whether the code calling it was expecting an Artist or a Set.

Furthermore, even if you could, according to your comments on various other answers, you actually have a requirement to pass an ArtistSet to a vendor-supplied function, meaning that function has not given the compiler or humans any clue as to what it is expecting. You are completely out of luck for any sort of technically correct answer.

As practical programming matter for getting the job done, I would do the following (in this order):

  1. File a bug report with whoever created an interface requiring ArtistSet and whoever generated the ArtistSet interface itself.
  2. File a support request with the vendor supplying the function requiring an ArtistSet and ask them what they expect the behavior of complement() to be.
  3. Implement the complement() function to throw an exception.
public class Sybil implements ArtistSet {
  public void complement() { 
    throw new UnsupportedOperationException('What am I supposed to do'); 
  }
  ...
}

Because seriously, you don't know what to do. What would be the correct thing to do when called like this (and how do you know for sure)?

class TalentAgent {
    public void pr(ArtistSet artistsSet) {
      artistSet.complement();
    }
}

By throwing an exception you have a chance at getting a stack trace that gives you a clue as to which of the two behaviors the caller is expecting. With luck nobody calls that function, which is why the vendor got as far as shipping code with this problem. With less luck but still some, they handle the exception. If not even that, well, at least now you will have a stack trace you can review to decide what the caller was really expecting and possibly implement that (though I shudder to think of perpetuation a bug that way, I've explained how I would do it in this other answer).

BTW, for the rest of the implementation I would delegate everything to actual Artist and Set objects passed in via the constructor so this can be easily pulled apart later.

Community
  • 1
  • 1
Old Pro
  • 24,624
  • 7
  • 58
  • 106
  • The question is how to work around this. So far Gray's answer is the best, but I'm sure there are better ways. 1: The vendor wont change their code. 2: It's already clear what each complement method should do, the problem is the vendor didn't know that Java doesn't have an easy way to implement it for both interfaces. 3 will make the code not work. – Dog May 18 '13 at 14:46
  • @Dog 1: How do you know that 3 will make the code not work? In what context is the exception thrown? 2: It is painfully unclear what ArtistSet.complement() should do. 3: What would you do if you magically created the perfect solution for ArtistSet and found the vendor's function still had bugs? I would do exactly the same thing here after implementing the unsupported operation exception. If anyone complained that I should fix it I would explain, as you have, that it is in vendor's code that I can't change. – Old Pro May 18 '13 at 17:33
  • 3 will make the code not work because it will crash when it tries to call `complement`. `ArtistSet.complement` should crash (but not when called through an `Artist` or `Set` reference). – Dog May 22 '13 at 02:32
1

How can I implement a class which has two superinterfaces having homographic methods?

In Java, a class which has two superinterfaces having homographic methods is considered to have only one implementation of this method. (See the Java Language Specification section 8.4.8). This allows classes to conveniently inherit from multiple interfaces that all implement the same other interface and only implement the function once. This also simplifies the language because this eliminates the need for syntax and method dispatching support for distinguishing between homographic methods based on which interface they came from.

So the correct way to implement a class which has two superinterfaces having homographic methods is to provide a single method that satisfies the contracts of both superinterfaces.

C# has a way to do this. How can it be done in Java? Is there no construct for this?

C# defines interfaces differently than Java does and therefore has capabilities that Java does not.

In Java, the language construct is defined to mean that all interfaces get the same single implementation of homographic methods. There is no Java language construct for creating alternate behaviors of multiply-inherited interface functions based on the compile time class of the object. This was a conscious choice made by the Java language designers.

If not, how can it be done with reflection/bytecode tricks/etc most reliably?

"It" cannot be done with reflection/bytecode tricks because the information needed to decide which interface's version of the homographic method to choose is not necessarily present in the Java source code. Given:

interface I1 { 
    // return ASCII character code of first character of String s 
    int f(String s); // f("Hello") returns 72
}
interface I2 {
    // return number of characters in String s 
    int f(String s);  // f("Hello") returns 5
}

interface I12 extends I1, I2 {}

public class C {
  public static int f1(I1 i, String s) { return i.f(s); }  // f1( i, "Hi") == 72
  public static int f2(I2 i, String s) { return i.f(s); }  // f2( i, "Hi") == 2
  public static int f12(I12 i, String s) { return i.f(s);} // f12(i, "Hi") == ???
}

According to the Java language specification, a class implementing I12 must do so in such a way that C.f1(), C.f2(), and C.f12() return the exact same result when called with the same arguments. If C.f12(i, "Hello") sometimes returned 72 and sometimes returned 5 based on how C.f12() were called, that would be a serious bug in the program and a violation of the language specification.

Furthermore, if the author of class C expected some kind of consistent behavior out of f12(), there is no bytecode or other information in class C that indicates whether it should be the behavior of I1.f(s) or I2.f(s). If the author of C.f12() had in mind C.f("Hello") should return 5 or 72, there's no way to tell from looking at the code.

Fine, so I cannot in general provide different behaviors for homographic functions using bytecode tricks, but I really have a class like my example class TheaterManager. What should I do to implement ArtistSet.complement()?

The actual answer to the actual question you asked is to create your own substitute implementation of TheaterManager that does not require an ArtistSet. You do not need to change the library's implementation, you need to write your own.

The actual answer to the other example question you cite is basically "delegate I12.f() to I2.f()" because no function that receives an I12 object goes on to pass that object to a function expecting an I1 object.

Stack Overflow is only for questions and answers of general interest

One of the stated reasons to reject a question here is that "it is only relevant to an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet." Because we want to be helpful, the preferred way to handle such narrow questions is to revise the question to be more broadly applicable. For this question I have taken the approach of answering the broadly applicable version of the question rather than actually editing the question to remove what makes it unique to your situation.

In the real world of commercial programming any Java library that has a broken interface like I12 would not accumulate even dozens of commercial clients unless it could be used by implementing I12.f() in one of these ways:

  • delegate to I1.f()
  • delegate to I2.f()
  • do nothing
  • throw an exception
  • pick one of the above strategies on a per-call basis based on the values of some members of the I12 object

If thousands or even only a handful of companies are using this part of this library in Java then you can be assured they have used one of those solutions. If the library is not in use by even a handful of companies then the question is too narrow for Stack Overflow.

OK, TheaterManager was an oversimplification. In the real case it is too hard for me to replace that class and I don't like any of the practical solutions you've outlined. Can't I just fix this with fancy JVM tricks?

It depends on what you want to fix. If you want to fix your specific library by mapping all the calls to I12.f() and then parsing the the stack to determine the caller and choosing a behavior based on that. You can access the stack via Thread.currentThread().getStackTrace().

If you run across a caller you do not recognize you may have a hard time figuring out which version they want. For example you may be called from a generic (as was the actual case in the other specific example you gave), like:

public class TalentAgent<T extends Artist> {
  public static void butterUp(List<T> people) {
    for (T a: people) {
      a.complement()
    }
  }
}
 

In Java, generics are implemented as erasures, meaning all type information is thrown away at compile time. There is no class or method signature difference between a TalentAgent<Artist> and a TalentAgent<Set> and the formal type of the people parameter is just List. There is nothing in the class interface or method signature of the caller to tell you what to do by looking at the stack.

So you would need to implement multiple strategies, one of which would be decompiling the code of the calling method looking for clues that the caller is expecting one class or another. It would have to be very sophisticated to cover all the ways this could happen, because among other things you have no way of knowing in advance what class it actually expecting, only that it is expecting a class that implements one of the interfaces.

There are mature and extremely sophisticated open source bytecode utilities, including one that automatically generates a proxy for a given class at runtime (written long before there was support for that in the Java language), so the fact that there isn't an open source utility for handling this case speaks volumes about the ratio of effort to usefulness in pursuing this approach.

Community
  • 1
  • 1
Old Pro
  • 24,624
  • 7
  • 58
  • 106
  • 1
    TL;DR. OP asked how to best approximate what C# can do without hacks, clearly this is not a possible answer. "C# defines interfaces differently than Java does and therefore has capabilities that Java does not. " This is laughable. Clearly Java designers made a mistake. Calling this part of the design is retarded at best. This is a classic design mistake that leads to an uncomposable language. The only way to not get hit by the homographic methods issue is if there was a global central repository of interfaces with a lock around it (like the unicode consortium but for interfaces). – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Jun 29 '13 at 19:41
  • 2
    Oh wait I'm wrong. You don't need a central interface consortium, you just need to be sure that whenever you extend multiple interfaces, that they are disjoint. But since all the developers are like you would think checking disjointness every time they extend multiple interfaces is impractical, the problem will happen. Being "pragmatic" isn't composable. You can keep saying how the problem is unlikely, but it's happened to OP so spamming this crap isn't really helping anyone. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Jun 29 '13 at 19:53
  • @Longpoke, the OP asked how to implement in Java a C# construct unavailable in Java. In fact, this C# construct is not available in Python, Ruby, Objective C, PHP, or JavaScript, which together with Java, C++, and C# are the major Object Oriented Languages in current use for producing commercial software. In other words, the professional consensus is that the C++ and C# designers made a mistake allowing this construct because it leads to problems like the OPs. In any case, it's a waste of everyone's time to complain that a language doesn't have a feature you want. The OP should just use C#. – Old Pro Jun 29 '13 at 21:32
  • "the OP asked how to implement in Java a C# construct unavailable in Java." **exactly**. The professional consensus is a joke. (note how you included PHP and JavaScript in your list). Also Python's inheritance for example is not the same thing as superinterfaces, and beside that, Python is a very undisciplined language. Ruby is someone's fun project he did in his spare time. I find it amusing that you think languages shouldn't be designed logically because it will lead to OP asking how to use Java logically. Also OP never complained. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Jun 30 '13 at 14:57
  • You just restated the question, so either answer it, or GTFO. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Jun 30 '13 at 14:59
  • Oh, one more thing: GFYS for claiming that there is anything professional about those languages you listed. Are they used in the industry? Unfortunately, yes. Were they created on the fly by random Joe who knows absolutely nothing about programming language design: Yes. These languages are literally the equivalent of cancer. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Jun 30 '13 at 15:04
  • @Longpoke, Java was professionally designed by a highly qualified team that sought to learn from and avoid the problems in C++, and they were quite successful, which is why it has been so widely adopted. Only Microsoft uses C#, despite the availability of Mono. And I answered the question 7 different ways in 3 different posts, so write a better answer and stop whining. – Old Pro Jul 01 '13 at 18:27
  • Java is a joke, ML was there decades before and has none of the problems Java has aside from ambient authority (which is trivial to remove from ML, but not for Java). Unfortunately, the software industry is based around pseudoscience, so real goals are not even attempted to be solved in contemporary languages such as Java. A perfect example of this idiocy is that in Java you use the Visitor Pattern, checked exceptions, or huge DTOs, to emulate what you would do in one line with algebraic data types. The fact that Java pretends to have types is also quite hilarious. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Jul 02 '13 at 17:27
  • You are correct though that Java is a euphemism of C++, which is a more broken version of C, for people who conflate the ability to work with something convoluted with intellect. – L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳ Jul 02 '13 at 17:30
1

Okay, after much research, I have another idea to fully accommodate the situation. Since you can't directly modify their code... you can force the modifications yourself.

DISCLAIMER: The example code below is very simplified. My intention is to show the general method of how this might be done, not to produce functioning source code to do it (since that's a project in itself).

The issue is that the methods are homographic. So to solve it, we can just rename the methods. Simple, right? We can use the Instrument package to achieve this. As you'll see in the linked documentation, it allows you to make an "agent" which can directly modify classes as they're loaded or re-modify them even if they've already been loaded.

Essentially, this requires you to make two classes:

  • An agent class which preprocesses and reloads classes; and,
  • A ClassFileTransformer implementation which specifies the changes you want to make.

The agent class must have either a premain() or agentmain() method defined, based on whether you want it to begin its processing as the JVM starts up or after it is already running. Examples of this are in the package documentation above. These methods give you access to an Instrumenation instance, which will allow you to register your ClassFileTransformer. So it might look something like this:

InterfaceFixAgent.java

public class InterfaceFixAgent {

    public static void premain(String agentArgs, Instrumentation inst) {

        //Register an ArtistTransformer
        inst.addTransformer(new ArtistTransformer());

        //In case the Artist interface or its subclasses 
        //have already been loaded by the JVM
        try {
            for(Class<?> clazz : inst.getAllLoadedClasses()) {
                if(Artist.class.isAssignableFrom(clazz)) {
                    inst.retransformClasses(clazz);
                }
            }
        }
        catch(UnmodifiableClassException e) {
            //TODO logging
            e.printStackTrace();
        }
    }
}

ArtistTransformer.java

public class ArtistTransformer implements ClassFileTransformer {

    private static final byte[] BYTES_TO_REPLACE = "complement".getBytes();
    private static final byte[] BYTES_TO_INSERT = "compliment".getBytes();

    @Override
    public byte[] transform(ClassLoader loader, String className,
                            Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
                            byte[] classfileBuffer) throws IllegalClassFormatException {

        if(Artist.class.isAssignableFrom(classBeingRedefined)) {
            //Loop through the classfileBuffer, find sequences of bytes
            //which match BYTES_TO_REPLACE, replace with BYTES_TO_INSERT
        }
        else return classfileBuffer;
    }

This is, of course, simplified. It will replace the word "complement" with "compliment" in any class which extends or implements Artist, so you will very likely need to further conditionalize it (for example, if Artist.class.isAssignableFrom(classBeingRedefined) && Set.class.isAssignableFrom(classBeingRedefined), you obviously don't want to replace every instance of "complement" with "compliment", as the "complement" for Set is perfectly legitimate).

So, now we've corrected the Artist interface and its implementations. The typo is gone, the methods have two different names, so there is no homography. This allows us to have two different implementations in our CommunityTheatre class now, each of which will properly implement/override the methods from the ArtistSet.

Unfortunately, we've now created another (possibly even bigger) issue. We've just broken all the previously-legitimate references to complement() from classes implementing Artist. To fix this, we will need to create another ClassFileTransformer which replaces these calls with our new method name.

This is somewhat more difficult, but not impossible. Essentially, the new ClassFileTransformer (let's say we call it the OldComplementTransformer) will have to perform the following steps:

  1. Find the same string of bytes as before (the one representing the old method name, "complement");
  2. Get the bytes before this which represent the object reference calling the method;
  3. Convert those bytes into an Object;
  4. Check to see if that Object is an Artist; and,
  5. If so, replace those bytes with the new method name.

Once you've made this second transformer, you can modify the InterfaceFixAgent to accommodate it. (I also simplified the retransformClasses() call, since in the example above we perform the needed check within the transformer itself.)

InterfaceFixAgent.java (modified)

public class InterfaceFixAgent {

    public static void premain(String agentArgs, Instrumentation inst) {

        //Register our transformers
        inst.addTransformer(new ArtistTransformer());
        inst.addTransformer(new OldComplementTransformer());

        //Retransform the classes that have already been loaded
        try {
            inst.retransformClasses(inst.getAllLoadedClasses());
        }
        catch(UnmodifiableClassException e) {
            //TODO logging
            e.printStackTrace();
        }
    }
}

And now... our program is good to go. It certainly wouldn't be easy to code, and it will be utter hell to QA and test. But it's certainly robust, and it solves the issue. (Technically, I suppose it avoids the issue by removing it, but... I'll take what I can get.)

Other ways we might have solved the problem:

Both of these would allow you to directly manipulate bytes in memory. A solution could certainly be designed around these, but I believe it would be much more difficult and much less safe. So I went with the route above.

I think this solution could even be made more generic into an incredibly useful library for integrating code bases. Specify which interface and which method you need refactored in a variable, a command line argument, or a configuration file, and let her loose. The library that reconciles conflicting interfaces in Java at runtime. (Of course, I think it would still be better for everyone if they just fixed the bug in Java 8.)

asteri
  • 11,402
  • 13
  • 60
  • 84
  • This should work for all cases except when there is an already existing `ArtistSet` implementation which you didn't yourself make. But it's reasonable to assume there are no existing implementations, since it's impossible to implement unless they did a workaround like this. The only thing I'm not sure of is that when some bytecode calls complement, does it capture the type of the object? I'm guessing yes. I'll try and implement this when I have time and see how far I get. Also I wonder whether it's a legal problem to modify someone's code at runtime. Still, best answer so far. – Dog Aug 17 '13 at 18:18
  • 1
    @Dog Actually a really good point about the legality. You'd probably have to get your vendors to sign off on it. If you work for a big corporation, I'm sure legal wouldn't be thrilled. – asteri Aug 17 '13 at 22:53
0

Here's what I'd do to remove the ambiguity:

interface Artist {
    void complement(); // [SIC] from OP, really "compliment"
    int getEgo();
}

interface Set {
    void complement(); // as in Set Theory
    void remove();
    boolean empty(); // [SIC] from OP, I prefer: isEmpty()
}

/**
 * This class is to represent a Set of Artists (as a group) -OR-
 * act like a single Artist (with some aggregate behavior).  I
 * choose to implement NEITHER interface so that a caller is
 * forced to designate, for any given operation, which type's
 * behavior is desired.
 */
class GroupOfArtists { // does NOT implement either

    private final Set setBehavior = new Set() {
        @Override public void remove() { /*...*/ }
        @Override public boolean empty() { return true; /* TODO */ }            
        @Override public void complement() {
            // implement Set-specific behavior
        }
    };

    private final Artist artistBehavior = new Artist() {
        @Override public int getEgo() { return Integer.MAX_VALUE; /* TODO */ }            
        @Override public void complement() {
            // implement Artist-specific behavior
        }
    };

    Set asSet() {
        return setBehavior;
    }

    Artist asArtist() {
        return artistBehavior;
    }
}

If I were passing this object to the HR department, I'd actually give it the value returned from asSet() to hire/fire the entire group.

If I were passing this object to the Theater for a performance, I'd actually give it the value returned from asArtist() to be treated as talent.

This works as long as YOU are in control of talking to the different components directly...

But I realize that your problem is a single third-party vendor has created a component, TheaterManager, that expects one object for both of these functions and it won't know about the asSet and asArtist methods. The problem is not with the vendors that created Set and Artist, it is the vendor that combined them instead of using a Visitor pattern or just specifying an interface that would mirror the asSet and asArtist methods I made above. If you can convince your one vendor "C" to fix that interface, your world will be a lot happier.

Good luck!

William Price
  • 4,033
  • 1
  • 35
  • 54
  • Good comment, but the problem is that the people who wrote ArtistSet didn't know Java would merge the methods of the two superinterfaces, so they wouldn't have thought of writing the class this way. Why did you write it this way? Just to avoid the name conflict, or is multiple superinterfaces just bad in general? – Dog May 19 '13 at 16:00
  • 1
    There's nothing wrong with multiple superinterfaces on their own, as long as they don't overlap functionality OR where there _is_ overlap they are semantically compatible. That's not the case here, and it really _is_ the fault of the ArtistSet author -- whether they understood the conflict originally doesn't change the fact that their Java implementation has a design flaw and should be fixed. I have sympathy for the OP facing this issue. – William Price May 19 '13 at 17:26
  • Since the two interfaces overlap in the "complement" [SIC] method in a way that is mutually exclusive, I simply _cannot_ implement both interfaces: one of them would be wrong. What I _could_ have done is implement one interface (the one I think would be used most often) and then use the "encapsulated behavior" approach as I did above for the other interface. At least this way I wouldn't propagate the "hidden" ambiguity/mistake further into my own code, but I admit it's not a solution for the OP's need to integrate with 3rd-party libraries. It's an example of how the vendor could fix it. – William Price May 19 '13 at 17:33
-1

Dog, I have a strong feeling you are leaving out some details that are crucial to the solution. This often happens on SO because

  • people need to leave out a lot of details to get the question to a reasonable size and scope,
  • people do not fully understand the problem and the solution (which is why they are asking for help) so they cannot be sure which details are important and which are not, and
  • the reason the person cannot solve the problem on their own is because they do not understand the importance of this detail, which is the same reason they left it out.

I've said in another answer what I would do about ArtistSet. But keeping the above in mind I will give you another solution to a slightly different problem. Lets say I had code from a bad vendor:

package com.bad;

public interface IAlpha {
    public String getName();
    // Sort Alphabetically by Name
    public int compareTo(IAlpha other);
}

This is bad because you should declare a function returning a Comparator<IAlpha> to implement the sorting strategy, but whatever. Now I get code from a worse company:

package com.worse;
import com.bad.IAlpha;

// an Alpha ordered by name length
public interface ISybil extends IAlpha, Comparable<IAlpha> {}

This is worse, because it is totally wrong, in that it overrides behavior incompatibly. An ISybil orders itself by name length, but an IAlpha orders itself alphabetically, except an ISybil is an IAlpha. They were mislead by the anti-pattern of IAlpha when they could and should have done something like:

public interface ISybil extends IAlpha {
  public Comparator<IAlpha> getLengthComparator();
}

However, this situation is still much better than ArtistSet because here the expected behavior is documented. There is no confusion about what ISybil.compareTo() should do. So I would create classes as follows. A Sybil class that implements compareTo() as com.worse expects and delegates everything else:

package com.hack;

import com.bad.IAlpha;
import com.worse.ISybil;

public class Sybil implements ISybil {

    private final Alpha delegate;

    public Sybil(Alpha delegate) { this.delegate = delegate; }
    public Alpha getAlpha() {   return delegate; }
    public String getName() { return delegate.getName(); }
    public int compareTo(IAlpha other) {
        return delegate.getName().length() - other.getName().length();
    }

}

and an Alpha class that works exactly like com.bad said it should:

package com.hack;
import com.bad.IAlpha;

public class Alpha implements IAlpha {
    private String name;
    private final Sybil sybil;
    public Alpha(String name) { 
        this.name = name;
        this.sybil = new Sybil(this);
    }

    // Sort Alphabetically
    public int compareTo(IAlpha other) {
        return name.compareTo(other.getName());
    }

    public String getName() { return name; }
    public Sybil getSybil() { return sybil; }
}

Note that I included type conversion methods: Alpha.getSybil() and Sybil.getAlpha(). This is so I could create my own wrappers around any com.worse vendor's methods that take or return Sybils so I can avoid polluting my code or any other vendor's code with com.worse's breakage. So if com.worse had:

public ISybil breakage(ISybil broken);

I could write a function

public Alpha safeDelegateBreakage(Alpha alpha) {
  return breakage(alpha.getSybil).getAlpha();
}

and be done with it, except I would still complain vociferously to com.worse and politely to com.bad.

Old Pro
  • 24,624
  • 7
  • 58
  • 106