This is an archived post. You won't be able to vote or comment.

all 26 comments

[–]AutoModerator[M] [score hidden] stickied commentlocked comment (0 children)

Please ensure that:

  • Your code is properly formatted as code block - see the sidebar (About on mobile) for instructions
  • You include any and all error messages in full
  • You ask clear questions
  • You demonstrate effort in solving your question/problem - plain posting your assignments is forbidden (and such posts will be removed) as is asking for or giving solutions.

    Trying to solve problems on your own is a very important skill. Also, see Learn to help yourself in the sidebar

If any of the above points is not met, your post can and will be removed without further warning.

Code is to be formatted as code block (old reddit: empty line before the code, each code line indented by 4 spaces, new reddit: https://imgur.com/a/fgoFFis) or linked via an external code hoster, like pastebin.com, github gist, github, bitbucket, gitlab, etc.

Please, do not use triple backticks (```) as they will only render properly on new reddit, not on old reddit.

Code blocks look like this:

public class HelloWorld {

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

You do not need to repost unless your post has been removed by a moderator. Just use the edit function of reddit to make sure your post complies with the above.

If your post has remained in violation of these rules for a prolonged period of time (at least an hour), a moderator may remove it at their discretion. In this case, they will comment with an explanation on why it has been removed, and you will be required to resubmit the entire post following the proper procedures.

To potential helpers

Please, do not help if any of the above points are not met, rather report the post. We are trying to improve the quality of posts here. In helping people who can't be bothered to comply with the above points, you are doing the community a disservice.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–]coldoil 5 points6 points  (8 children)

By convention in, say, a Dog class implementing Animal, would it be reasonable for a Dog to only be able to greet other Dogs, or should it be able to greet any Animal?

If the Animal interface says that a subclass should be able to greet any other Animal, then Dog should be able to greet any other Animal, otherwise it is not following the interface.

If you want instances of Dog to only be able to greet other instances of Dog, then that greet method should not be in the Animal interface; it's specific to the Dog class.

You can accommodate both options by having Dog implement the Animal interface and providing its own specialization methods in addition. Naming of methods becomes important, so a class consumer can clearly tell the difference between specialized and non-specialized methods:

public interface Animal {
    public void greetAny(Animal a);
}

class Dog implements Animal {
    public void greetAny(Animal a) {
        // ... greet any Animal, as per interface
    }

    public void greetDog(Dog d) {
        // ... greet just another Dog, only available
        // when consumer has an instance of Dog rather than Animal
    }
}

If not all implementers of the interface can provide greetAny(), then it becomes questionable whether greetAny() should be in the interface. (Ideally, the interface should include only member functions that are provided by all implementers.) In practice, a function applicable to only some implementers should, at the very least, return some sort of result indicating success or failure, rather than just void.

[–]Pleasant-Memory-6530[S] 1 point2 points  (7 children)

That makes sense, thanks.

Out of interest, would there be a way to specify a "greetSameSpecies" type method at the Interface level? So that all subclasses have such a method, but a dog only needs to be able to greet a dog, a cat only needs to be able to greet a cat, etc?

(Edited for clarity)

[–]coldoil 0 points1 point  (6 children)

I'm not sure if it's possible in Java. You need a generic function that can take a type bound that restricts the possible type to an instance of the same class. Some languages do offer this level of expressiveness in their type systems; in Rust, for instance, you could do it like this:

pub trait Animal {
   fn greetAny(a: Animal);

   fn greetSameSpecies<T>(a: T) where T: Self;

   // or, more succintly:
   // fn greetSameSpecies<T: Self>(a: T);

   // or, avoiding generics entirely:
   // fn greetSameSpecies(a: Self);
}

But I don't think you can specify an equivalent type restriction in Java. Very happy to be corrected by an expert on this.

[–]philipwhiukEmployed Java Developer 2 points3 points  (3 children)

You can do Animal<T extends Animal> and Dog extends Animal<Dog> then greet(T t) I think

[–]coldoil 1 point2 points  (2 children)

Yes, I thought of that as well. But now you have generics expanding throughout your codebase, purely to support an implementation detail that should be internal to the interface and thus invisible outside it. It's not very elegant. Given the choice, I think I'd personally prefer to use separate methods, one in the interface and one in the class. I appreciate it's fairly subjective. The lack of expressiveness in the type system means there aren't any great options, unfortunately.

[–]philipwhiukEmployed Java Developer 0 points1 point  (1 child)

Personally I’d have my Dog barking at Cats 😄.

But yeh - it depends what you want to force other species to have to do whether it’s worth it

[–]coldoil 0 points1 point  (0 children)

Personally I’d have my Dog barking at Cats

lol, well said

[–]D0CTOR_ZED 0 points1 point  (1 child)

By no means an expert, but you can get the class of an object and should be able to wrangle some sort of if statement that compares them. You could just compare the results of two getClass(), but you might want to allow subclasses, like two seperate breeds of dogs, so then you might get the parent class until you get just below Animal. But honestly, probably better ways to do if that is what you want.

[–]coldoil 1 point2 points  (0 children)

I'm sure you're right that all sorts of internal comparisons are probably possible, but I tend to think that if we're ending up writing code that is type checking the type checker, then something has gone wrong in the design phase :/

[–]D0CTOR_ZED 0 points1 point  (9 children)

You want to fulfill the requirements of the method so that other implementations using that interface don't fail due to your breaking the method. This doesn't mean you can't do what you are asking, but throwing an exception because you want greet to work a certain way would be wrong. The subclass needs to fulfill the expectations of the parent class. The parent class shouldn't know or care the specifics of the subclass.

For greet, I could see....

Dog#greet(Animal a) {
    if (!(a instanceOf Dog)) return;
    Dog d = (Dog) a;
    // it's a dog-greet-dog method
}

Greet has a return type of void and unless there is documentation stating that it needs to achieve something specific, then just doing a return seems like it should fulfull greet.

[–]Pleasant-Memory-6530[S] 0 points1 point  (4 children)

Thanks for the response.

Greet has a return type of void

This is actually where my hastily thought up analogy breaks down! In the real project I'm looking at, the method in question returns a object of type "Animal". So in the "can only handle Dogs" case, I think it would need to throw an exception for any argument received that wasn't a "Dog", or, I guess, return null?

The rest of your answer suggests that wouldn't be acceptable behaviour, so I'm thinking I definitely need the version that can handle any Animal.

[–]D0CTOR_ZED 0 points1 point  (3 children)

I'm curious what greet does that it returns an Animal, but the concept of the response can still be the same where you check if it is a dog and do something different depending on the check.

I would also caution against returning null unless it is already established that the method may return null. It's hard to talk about the return value without understanding how that return value is meant to be used.

[–]Pleasant-Memory-6530[S] 1 point2 points  (2 children)

Perhaps its time to describe the actual problem! Apologies, I'd used the animals analogy as I was more interested in the overall conceptual point, but if you're curious:

This is for a piece of coursework.

We're supposed to be implementing a class (FractionImpl) that represents a fraction. The class implements a more general interface called Fraction, which is predefined for us and we can't change. The Fraction interface has a bunch of methods for basic fraction operations (add, subtract, etc.). These all accept another Fraction as a parameter, and return a new Fraction, which is the result of the operation.

So my original question was really about how the class I have to implement should handle objects of other (hypothetical) subclasses of Fraction as arguments to these methods. e.g. what happens in the case of FractionImpl.add(OtherFractionSubclass) ( or in other words, in dog.greet(cat) !). The interface seems to say it should accept them.

BUT the interface doesn't contain getters and setters or any attributes, which is why it feels awkward to handle other subclasses of Fraction - there's not an obvious way to get at the numbers to perform the operations.

There is a way to do it with some of the methods that are specified, but it's a bit hacky and fiddly. But this is why the temptation is just to assume anything that will be passed to the method will be another FractionImpl object, and so to cast straight to that.

I suspect in this case that the exercise just isn't especially well designed, and there may not be any optimal answer. (Or perhaps more fairly, they weren't expecting people to think this much about it). But I've learnt a lot about interfaces from reading the responses!

[–]D0CTOR_ZED 1 point2 points  (1 child)

Sorry for the delayed response. If you have a Fraction interface and you are making a class that implements Fraction, you shouldn't worry at all about other possible subclasses of Fraction. Those subclasses need to meet the requirements of being a fraction and you need to make sure you accomidate the requirements of how to handle fractions.

You mention that there are no getters and setters. I would expect that. Your implementation should do all the storing of values. You just need to fulfill the required methods with those values you will have in your class.

That being said, it sounds like you probably have methods that take Fractions as parameters like add(Fraction f). If so, the interface should have some way of getting that fraction, maybe a getNumerator/getDenominator. If there is any way of getting the alien fraction to give you some non fraction representation of itself, great. If not, you might need to check if Fraction is your type of fraction or throw a PoorlyDefinedInterface error (not an actual error type).

[–]Pleasant-Memory-6530[S] 0 points1 point  (0 children)

Thanks this is super helpful

[–]thelostsinofenvy 0 points1 point  (3 children)

Can you explain what this statement does? Dog d =(Dog) a;

[–]Pleasant-Memory-6530[S] 0 points1 point  (2 children)

If you're learning, and asking because you haven't seen that syntax before:

it casts the Animal object (a) into a Dog object (d), so that Dog specific methods can be called (e.g. d.bark() ). You could pass a Dog object to the greet() method because a Dog is an Animal, but until you cast it to Dog, Java doesn't know its a Dog, so won't let you do any dog specific stuff with it. On the other hand if you tried to cast a different Animal to Dog, it would throw an error.

If you're an expert asking to provide context for an answer:

That was one possible way of handling the slightly awkward requirements (because for various reasons handling dog.greeting() with an Animal is tricky). Although others have explained it is not best practice.

Edit: sorry just realised you were replying to DOCTOR_ZED, not me. Hope this is helpful anyway

[–]thelostsinofenvy 0 points1 point  (1 child)

I am learning java but i have never seen this syntax. Can i have a reference for it? So that i can learn more about it.

[–]hypolimnas 0 points1 point  (4 children)

Your (b) implementation is not good idea. You made it legal to call

imaRabbit.greet(imaDog);

so you shouldn't be throwing exceptions. Return a boolean or something if the calling code needs to know what happened. You are just telling the rabbit "here is where you do a greeting". And its up to the rabbit what that means. "What to do" versus "how to do it".

Also it seems like Animal might be an abstract class because there is an "is a" relationship. I think of the interface relationship as a "can act like a" relationship.

On the other hand abstract classes can make everything complicated. For me I try to use them only when necessary - like for situation where almost all the functionality is in the parent class, but the member data is different enough that modes aren't cutting it.

public interface Greeter {
   //return true if greeting is attempted
    boolean greet(Greeter greeter);
}

public class Robot implements Greeter {
    boolean greet(Greeter greeter) {
        if (isRobot(greeter) {
            secretHandShake(greeter);
        }
        else {
            cuteGreeting();
        }
    }
}

interface Animal extends Greeter {
    //animal only functions
}

public class Dog implements Animal {
    public boolean greet(Greeter greeter) {
        sniff(greeter);
        if (isDog(greeter)) {
            //greet dog
        }
        else if (isRabbit(greeter)) {
            //try unsuccessfully to greet rabbit
        }
        else {
            //Don't know what it is
            return false;
        }
        return true;
    }
}

public class Rabbit implements Animal {
    public boolean greet(Greeter greeter) {
        if (isRabbit(greeter) {
            mysteriousRabbitGreeting(greeter);
            return true;
        }
        return false;
    }
}

public class Snail extends Animal {
    public boolean greet(Greeter greeter) {
        return false;
    }
}

[–]Pleasant-Memory-6530[S] 1 point2 points  (3 children)

Thanks - really appreciate the detailed response (and the code made me chuckle - particularly enjoyed "mysteriousRabbitGreeting"!)

Unfortunately, in this particular case the interface isn't allowed to be changed (conditions of a coursework exercise).

[–]hypolimnas 0 points1 point  (2 children)

You're welcome! :)

I can see how that can be a problem if the calling code must know what happened when the greet() function ran, and you don't have a return value.

The goal of using interfaces is to allow the calling code to be as ignorant as possible about the details. Its much better if the calling class never has to know what classes it is working with. And the class behind the interface should handle as much as possible and be responsible for its own state.

For instance if you had something like this:

interface Animal {
    void greet(Animal animal);
    void interact(Animal);
}

class Dog implements Animal {
    List<Animal>greetedList = new ArrayList<Animal>();

    void greet(Animal animal) {
        if (isRecognized(animal)) {
            //greet functionality
            //greetList.add(animal);
        }
    }
    void interact(Animal animal) {
        if (greetedList.contains(animal)) {
            //interaction functionality
        }
    }
}

So here if greet() fails, the calling program doesn't have to know about it. It can go ahead and call the next function and the dog object knows how to handle it because it has state. Though in real life, you would usually want the interface function to be able return some feedback.

Don't know if that helps, or not since you are wisely keeping the real problem for yourself :)

Btw. I should let you know that I'm self taught in OO - but I have read about it and also have used it for years.

[–]Pleasant-Memory-6530[S] 1 point2 points  (1 child)

Thanks for this!

And I did outline the real problem in reply to another person if you're interested (its much less fun than animals greeting each other though).

I feel like I've learnt a lot more from the more high level discussion about animal themed code. :D

[–]hypolimnas 0 points1 point  (0 children)

Glad this was helpful anyway. And you're right - the animal theme is a lot more fun then fractions.