According to Why define a Java object using interface (e.g. Map) rather than implementation (HashMap), I know I should declare the most abstract type possible, so for example:
public interface Fruit{
}
public class Orange extends Fruit{
}
when declaring Orange, I should write "Fruit obj=new Orange();" instead of "Orange obj=new Orange();". However, does the "declare the most abstract type" version has higher coupling actually? Because I think "coupling" of a class is about counting how many other class names appeared in the source file, for example:
Declare the most abstract type : other class names appeared : Fruit, Orange, String (3 classes):
public static void main(String[] args){
Fruit obj=new Orange();
.
.
.
}
Declare the exact type : other class names appeared : Orange, String (2 classes):
public static void main(String[] args){
Orange obj=new Orange();
.
.
.
}
As "declare the most abstract type" contains 3 other class names, so I think the "declare the most abstract type" has higher coupling than the one that declare the exact type (only 2 class names). Is it true? If not, what is the misconception here? How can a class contains the name of other class but not depending on that class?
Note: the question is just about coupling, neither encourage nor discouraging "declaring most abstract type". Also another reason that I think "Fruit obj=new Orange();" has more coupling is, when one day Orange doesn't extends from Fruit anymore:
public class Orange{
}
"Fruit obj=new Orange();" needs changing to "Orange obj=new Orange();" to recompile, while "Orange obj=new Orange();" doesn't need to.
mainfunction, asmainis really "glue" code. You'd actually have something like a separate class (or even just a function) that takes in a Fruit, and expresses all of it's logic in terms of Fruit (never mentioning any derived type). That other class is decoupled from the concrete types. Note however, that just having a class extend an interface is not enough to decouple its clients from it. It's the kinds of methods you choose to put on that interface that allow for decoupling (or not). – Filip Milovanović Oct 25 '23 at 16:31Orangeclass is already coupled to the Fruit interface. That is that changes to theFruitinterface will generally require changes to theOrangeclass. By depending onOrange, you've got a transitive dependency on Fruit whether you import it or not. – JimmyJames Oct 25 '23 at 20:04Fruitand not in any explicit way onOrangeyou are so loosely coupled toOrangethat you are independently deployable. – candied_orange Oct 26 '23 at 16:51Orangeif it isn't a validFruit? – JimmyJames Oct 26 '23 at 17:01Orangeexists. Both the using code andOrangeknowFruitexists but not each other.Fruitdoesn't know either one exists. Do it that way and different teams of Devs can develop these independently so long as they agree onFruit. – candied_orange Oct 26 '23 at 17:11Fruit(the only scenario that matters) you will need changeOrangeor your compilation will fail, or you will get a runtime error. – JimmyJames Oct 26 '23 at 17:15Fruitusing code that has never heard of anOrange. It wont do much until some other complication unit hands it anOrangebut it will compile just fine. – candied_orange Oct 26 '23 at 17:22Orangewon't compile. How are you going to load theOrangeclass that isn't consistent with yourFruitinterface? Whether you explicitly depend onFruitis irrelevant. – JimmyJames Oct 26 '23 at 17:32Orangeknows aboutFruit. Will it help if I diagram it?UsingCode->Fruit<|-Orange. Notice that nothing is pointing atOrange? If you don't point at it you can be compiled without it. – candied_orange Oct 26 '23 at 18:02Orangehas an implicit dependency onFruit. This isn't controversial. It's part of the definition ofOrange. – JimmyJames Oct 26 '23 at 19:41OrangewithoutFruit. I'm not. I'm talking about compilingUseingCodewithoutOrange. I've tried to make this clear 5 different ways now. I'm starting to lose faith in my ability to communicate. – candied_orange Oct 26 '23 at 20:00