[SOLVED] java - constructor and varaible instantiation
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Hi, I'm new to java language. Consider this class A. It looks fine.
Code:
public class A {
public List<String> lstStr;
public int count;
public A() {
lstStr = new ArrayList<String>();
count = 0;
}
}
Suppose I need to reuse the list and member variables. I need a reset() function:
Code:
public class A {
public List<String> lstStr;
public int count;
public A() {
lstStr = new ArrayList<String>();
reset();
}
public void reset() {
lstStr.clear();
count = 0;
}
}
Now I need a class B that extends A. Similarly I need a reset() for the member variables:
Code:
public class B extends A {
public List<Double> lstDbl;
public double length;
public B() {
lstDbl = new ArrayList<Double>();
reset(); /*line X*/
}
@Override
public void reset() {
lstDbl.clear(); /*line Y*/
length = 0.0;
}
}
Got stuck here.
Observations:
1. line Y throws nullpointer exception upon an instance of B is new'ed.
2. A.reset() is hidden and never called.
3. line X seems repeated because A() will call reset() anyway. But it looks more complete with it.
So how to tackle this kind of typical reset() problem?
Or what is the correct way to initialize and reset?
Should I take away reset() from the ctor and rely on the user to reset() before every use?
Code:
public class A {
public List<String> lstStr;
public int count;
public A() {
lstStr = new ArrayList<String>();
}
public void reset() {
lstStr.clear();
count = 0;
}
}
public class B extends A {
public List<Double> lstDbl;
public double length;
public B() {
lstDbl = new ArrayList<Double>();
}
@Override
public void reset() {
super.reset();
lstDbl.clear();
length = 0.0;
}
}
class A {
public List<String> lstStr;
public int count;
public A() {
lstStr = new ArrayList<String>();
my_reset();
}
private void my_reset() {
lstStr.clear();
count = 0;
}
public void reset() {
my_reset();
}
}
class B extends A {
public List<Double> lstDbl;
public double length;
public B() {
lstDbl = new ArrayList<Double>();
}
@Override
public void reset() {
super.reset();
lstDbl.clear();
length = 0.0;
}
}
From a design standpoint, if you find yourself defining subclasses that "[have to] know about the guts of" the workings of their parents, then you probably ought to re-think that design. A particular telltale sign is when classes have many things that are declared to be public
Instead of thinking of the classes as mere "data containers," exposing their inner workings (their variables ...) for all the world to see and therefore to mess-with, think of them as "service providers."
For instance, objects of your class A, or any descendent thereof, provide the service of storing 'things' in some kind of a list-wise fashion. These objects so-far provide three services to all who come:
A "count" property, which tells you the number of 'things' being stored.
A "List" property, which gives access to the 'things' themselves.
A "reset" method, which empties the "List."
Parent-class "A" is, in fact, an abstract method: it defines all of the things which all of its children must do, must provide, and therefore must implement ... but it does not specify the implementation, itself. (It also should not specify the data type of any particular child-class, since this is obviously intended to vary from child to child, as being probably the only significant difference between the various children.)
The implementation of all of the child-methods must be compatible with what their abstract ancestor has set forth. (The language, whatever language it is, will see to that ...) But that implementation should be concealed. You don't know exactly how it was built: you can only see and use what it does. Code outside of the class cannot directly affect the implementation of the class; can't dabble with things that it has no business dabbling with; and can't invoke the properties and methods in the "wrong" way. A well-designed class structure leverages the ability of the language to reward mistakes with compile-time errors.
// The below is what I need to know about A.
class A {
public void reset() { ... } // guarantee to reset A to its initial state
}
// I need to write my own B that extends A:
class B extends A {
private List<Double> lstDbl;
private int resetCount = 0;
public B() {
reset();
}
public void reset() { // guarantee to reset B to its initial state
super.reset();
if (lstDbl == null) lstDbl = new ArrayList<Double>();
lstDbl.clear();
++resetCount;
}
}
This piece of code works fine. But resetCount is not quite controllable. Although trivial, it has impact to memory and CPU. When trivial things are ignored, how important can we become?
that depends on the number of instances. Actually you may try to print its value before exiting to see that. Probably also there can be multithreading issue.
Since B does not care or should not care the implementation of A, B can only rely on A to do its job. So it is not up to B to control when A should call reset(): A may or may not already called reset() in its own ctor.
I guess it is a price to pay when B chooses to override reset().
What you have proposed here is, of course, "neither right nor wrong," but I did want to very briefly clarify what I was referring to when I called class A an "abstract (parent ...) class."
In my experience, designers often like to "first, define a general rule" in the parent classes, which are abstract, and then to define the several children which they actually use in their code.
To clarify this idea, I'm going to introduce some new class-names:
"A" becomes Abstract_ListContainer.
A child-class corresponding to the present concrete-implementation of "A" becomes ListContainer_Integer.
"B" becomes ListContainer_Float.
The Abstract_ListContainer abstract class becomes a single conceptual umbrella under which you can "typecast" all of the ListContainer behaviors. Every "ListContainer," in order to be called a "ListContainer," will conform to these specifications, varying only in their particulars (namely: the data-type that it is prepared to store).
The two child-classes, then, are concrete implementations of the abstract design-pattern. They're the ones that actually get down to brass-tacks ... as they must, of course, since they're the only ones that will ever actually be instantiated.
"And so, why, exactly, do we go to all this trouble?" There are, I think, two excellent reasons:
(1) Human expressiveness: It is a quite-natural thing for us ... us humans ... to think in terms of "first, a sweeping generalization ... then, specifics." When first we wave our hands in the air, we exploit our extraordinary powers of abstract visualization (which a digital computer, of course, knows nothing of). Then, and only then, do we "get down to details."
(2) Compile-time error detection: Programming errors are very subtle, so we want to harness the computer's utterly mindless abilities, in order to flush-out as many runtime error situations as possible, when we compile. So that we never create erroneous code.
Thank you. Your reply helps me rethink about OO languages and the work I have done so far.
Instead of extending a MountainBike from a RoadBike because of shorter development time, I should be planning Vehicle seriously from the beginning.
---
Regarding the reset() observation, I have asked a friend from a Java background and his reply is that java people seldom think about 'reset' but instead a new instance every time a fresh start is needed. They don't bother to clear and reuse a list variable but instead assign a new list to the same variable and let GC handle the dangling allocation. That's what java is. If detailed performance and resource requirements are in place, C or C++ may be the more appropriate choice.
Just some personal comments:
I love Assembly but poor at it.
I love C/C++ and ok at it.
I hate Java but need to learn and live with it.
Agree that the question is unanswered and still bleeding. I'll keep it open for a while.
In a sense the code #4 is designed to have two different entry points of resetting by A. A::A() is not calling reset() so that B::reset() won't be called at initialization. Although not natural to write it that way, it works too.
Code:
// This is what Java people (my friend) do:
B b;
b = new B();
... // use b
b = new B();
... // use b again. b is like being reset. Java people don't bother with reset.
b = new B();
... // and again and again.
Code:
// This is what C++ people (I) do:
B *b = new B;
... // use b
b->reset(); // this is what I do for control.
... // use b again
b->reset();
... // and again and again.
delete b;
I wouldn't go so far as to say, Vehicle. Bicycle will do nicely.
(Also: everything I am saying applies to all languages of this sort ... pick your poison.)
Now, let's pursue that bicycle-idea just a little bit. You can usefully describe "a generic bike," although you can never buy one. Ex minimis, every "bike" has certain properties that are common to all "bikes," and certain things (ride(), park(), crash()) that you can do with them or to them.
It might even be useful to define some of these properties or activities "generically," placing these in the Bicycle class, and perhaps defining these so that they invoke methods or access properties that are defined (must be defined ...) by specific subclasses such as RoadBike. (Maybe, those properties/methods are protected so that they can only be accessed by such means; they can't be accessed directly "from the outside." In the parent class, these methods/properties are abstract so that the language, itself, requires that the descendents must override them.)
Sometimes, it's advantageous to say, "RoadBike does things exactly like any Bike does ... except ..." In situations like that, a child class can override the property/method of its ancestor, and then, within that handler, invoke the handler of its ancestor.
But what you want to be very careful of, IMHO, is: "diddling with the guts of anything." Once I have found the piece-of-code that is responsible for doing something, I don't want to be surprised by anything that I cannot see "in that particular set of source-code paragraphs." If I've determined that a bug is manifesting itself in this-or-that location, then I want the insect to be ... there. If there is "an exception to the rule," then I want the source-code to very plainly say that, and I want the language compiler to be my "enforcer," such that source-code which does not meet those strictures cannot compile. (Ergo, if it does compile, it must conform.)
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.