Suppose I have a class hierarchy.
At the head of the hierarchy is class A, followed by B, C, and so on. Inherited from each other.
Is it appropriate to do this?

A obj = new B(); //(Расширение) B obj = new A(); //(Сужение) 
  • Expansion - yes, contraction - no. - Nofate

2 answers 2

You can not assign ancestor heir. Only if you ancestor (a pointer to an ancestor object) previously assigned a pointer to a descendant object, then you can do the reverse assignment.

UPD

  class A { ... } class B extends A { ... } ... B b = new B(); A a = b; // можно B c = (B) a; // можно A a1 = new A(); B b1 = a1; // нельзя 
  • it would be better if you showed by the example of the code what is possible and what is not, it will be so clearer - jmu
  • Narrowing nizya - Vladimir Gordeev
  • @jmu added answer - alexlz

Think differently. Expansion-narrowing is too abstract.

The derived type is always a more specific variant of the base type. Let instead of the base type be “Fish”, and instead of the derivative - “Herring”.

Then it is clear what to write

 Рыба р = new Селёдка(); 

possible: your р - fish, for example, herring, why not.

But write the opposite:

 Селёдка с = new Рыба(); 

you can not: the fish is not necessarily herring, right?

You can use type conversion:

 // у вас есть какая-то рыба Рыба р; // тут много кода // а здесь вы точно знаете, что ваша рыба на самом деле селёдка // тогда можно использовать приведение типов Селёдка с = (Селёдка)р; // но если вы ошиблись, и в прошлой строке рыба была на самом деле // акулой, вы получите exception 

How to check if your fish is herring? This is not necessary: Селёдка Рыбы heir, that is, each Селёдка necessarily Рыба .

How to check if your fish is herring? Very simple:

 Рыба р; // много кода if (р instanceof Селёдка) { // о, оказывается, наша рыба -- селёдка! Селёдка с = (Селёдка)р; // здесь исключения не будет } 
  • @VladD I'd rather surrender. And then go into the definition ... lazy. In my opinion (it may not be the same as anything), strict static typing does not require type checking at runtime. Perhaps from the point of view of science, I'm wrong. Fully admit. Yes, and thanks for demonstrating that java-compiler is eating Russian letter identifiers. Did not know. - alexlz
  • @alexlz: I didn’t want to “conquer” you at all, it's just that I myself am interested in such questions. And your opinion about strict typing is interesting. Do not find clarifying questions for cavils. It seems to me that a single understanding of strict typing does not exist. Here, for example, is the opinion of Eric Lippert, one of the architects of C #: blogs.msdn.com/b/ericlippert/archive/2012/10/15/… - VladD
  • 2
    @alexlz: Returning to the topic of discussion, it seems to me that in a well-written program, the percentage of down-castes, instanceof, dynamic_cast <> and the like should be very small both in terms of code volume and in the number of executions during the program run time. Abuse of such structures usually means the wrong architecture of the project. - VladD 4:27
  • @VladD apologize too much, my illiteracy doesn’t bother me much. And the topic is not easy. And what about up-casts / down castes, so libraries of container classes without them will work? (In C ++, this went into the shadow after the introduction of template-templates, and once it also bloomed in full color). - alexlz 4:38
  • @alexlz: with the templates and the true down-caste is not needed. In Java, generics are implemented in such a way that in runtime containers contain simply references to Object, down-castes are inserted by the compiler, which is why performance does not benefit. In C #, in contrast to Java, the generics are made correctly, and contain real references to objects of the correct type, so the down-caste is not needed. The same is true in C ++, since each instantiation of a template is compiled separately. That is, down caste containers are not needed if the language allows it. Java, in this sense, loses both Sharpe and C ++, it is impossible to do without down castes in it. - VladD