Java - Change data type when using Getters & Setters - setter

As I stumbled across Encapsulation while learning Java. One of the benefits of using Getters and Setters, according to https://www.tutorialspoint.com/java/java_encapsulation.htm, is:
"The users of a class do not know how the class stores its data. A class can change the data type of a field and users of the class do not need to change any of their code."
But I'm not quite sure what this mean. Does it mean that a:
private String name;
can be changed to:
private int name; ?
I would be appreciate if someone could give an example regarding to this.

Yes, for example if there are only a fixed number of possible names, then maybe the class would change from:
class Thing
{
private String m_name;
....
public String getName()
{
return m_name;
}
...
}
to
class Thing
{
private static String[] POSSIBLE_NAMES = ...
private int m_nameIndex;
....
public String getName()
{
return POSSIBLE_NAMES[m_nameIndex];
}
...
}
The point is that the getter indicates that the object has a name and can tell it to you when you ask, but the object is free to store or calculate that name however it likes.

A class can change the data type of a field
I think this is wrong thing.its not data type its Access Modifiers when we using private access modifiers that variable visible to the class only. the public methods are the access points to this class's fields from the outside java world. Normally these methods are referred as getters and setters. Therefore any class that wants to access the variables should access them through these getters and setters.no need to change private String name; to public String name;

Related

How to use `Data members` in C#-9

I was trying the new syntax for C# 9 but I failed to use the Data members.
Documents say the syntax is:
public data class Person { string FirstName; string LastName; }
However, I faced the following compile errors:
CS0116: A namespace cannot directly contain members such as fields or methods
IDE1007: The name 'data' does not exist in the current context.
I have tried other syntaxes and they all worked. So, I am sure that I am using C#-9
Update: This question is suggested as a possible answer.
But, it is not my answer and I believe the accepted answer in that link is wrong. Record types and data members are two different things. Data members are a new way to define immutable types, while the record types are Value Objects.
Documents suggest that in the data member classes you just need to define the properties like private fields, so the Person is equal to:
public data class Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
data classes are now called record. They are the same, immutable objects which behave like value types (exhibiting structural or in other words value based equality).
Regarding your code in OP,
public data class Person { string FirstName; string LastName; }
could be rewritten as
public record Person(string FirstName, string LastName);
The above syntax uses Primary Constructor . If you would like to skip the primary constructor, you could also declare records as
public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
In either cases, the Properties, FirstName and LastName could be assigned only during initialization (either via Constructor or object initializers) and not after that.

Override #Column attribute value

I have several POJOs which will have a monetary amount. My idea is to create a generic object MonetaryAmount (consisting of a currency and a value), which will then be used whenever I want to represent a monetary amount in one of my POJOs:
public class MonetaryAmount {
private String currency;
private BigDecimal value;
}
public class Account {
#Column(name = "ACCOUNT_NAME")
private String name;
private MonetaryAmount balance; // TODO set column annotation values of currency and value
}
Since MonetaryAmount will be used in several POJOs, I couldn't annotate the currency and value attributes with the #Column since the column name will not always be the same in all cases. Is there any way to annotate MonetaryAmount attributes (e.g. balance in the example above) to provide the column name for the currency and value attributes in a way that jOOQ understands them when mapping/unmapping a POJO similar to how Hibernate interprets the #AttributeOverride annotation please?
The #Embeddable annotation is currently (jOOQ 3.11) not supported by jOOQ's DefaultRecordMapper yet. The relevant feature requests are:
https://github.com/jOOQ/jOOQ/issues/2360
https://github.com/jOOQ/jOOQ/issues/2530
https://github.com/jOOQ/jOOQ/issues/6518
What you can do already now, if you're not using the JPA annotations on your POJOs, is to use the following aliasing notation in your query:
ctx.select(
ACCOUNT.ACCOUNT_NAME.as("name"),
ACCOUNT.CURRENCY.as("balance.currency"),
ACCOUNT.VALUE.as("balance.value"))
.from(ACCOUNT)
.fetchInto(Account.class);
This feature is documented in DefaultRecordMapper, see:
If Field.getName() is MY_field.MY_nested_field (case-sensitive!), then this field's value will be considered a nested value MY_nested_field, which is set on a nested POJO that is passed to all of these (regardless of visibility):
Single-argument instance method MY_field(...)
Single-argument instance method myField(...)
Single-argument instance method setMY_field(...)
Single-argument instance method setMyField(...)
Non-final instance member field MY_field
Non-final instance member field myField
Assuming Hibernate : You can used Embedded components.
#Entity
public class Account implements Serializable{
#Column(name = "ACCOUNT_NAME")
private String name;
#Embedded
#AttributeOverrides( {
    #AttributeOverride(name="currency", column = #Column(name="CURRENCY") ),
    #AttributeOverride(name="value", column = #Column(name="VALUE") )
} ) private MonetaryAmount balance;
}
#Embeddable
public class MonetaryAmount implements Serializable{
private String currency;
private BigDecimal value;
}
Though this should work, I think in your case you should try inheritance and still use same approach to override attributes in Object Oriented way.

Exercise 115; should I use getters/setters or something else?

I took care of exercise 105. I don't know what to do with 115 though. I've worked a little bit more and progressed a slight bit, but here's the exercise:
A team of biologists is conducting an experiment that involves collecting data on animals founds in a 1 km square area of woodland. As each animal is identified, a record is made of its name, the time of its discovery, and the initials of the scientist who found it. The data are to be recorded on a laptop. Design and implement a system for storing the data, and test your code thoroughly.
[Hint: Think in terms of creating an object for each discovery. What information should each object store, and in what will you store these objects?]
Here is my code:
// put class definitions here
public class Record{
String name;
String initials;
String time;
public Record{
this.name = name;
this.initials = initials;
this.time = time;
}
}
Here's another section for me to test my solution in:
public static void main( String[] args )
{
// test your solution here
}
So I know I'm supposed to make a new Object out of the variables, but do I need to use getters and setters or something? If that was the case it would probably be a lot easier than I'm assuming.
Thx
Getters and setters is a common pattern for getting internal member variables from Objects. They are not mandatory, but it is a good practice.
You need to read some Java and Object Oriented book to understand why we use it that way. You need to understand why encapsulation is encouraged too.
public class Record{
protected String name;
protected String initials;
protected String time;
public Record{
this.name = name;
this.initials = initials;
this.time = time;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
//... etc.
}

Disadvantages of interface objected programming

class Person{
private String name;
private int age;
private String gender;
//......
}
class Student extends Person{
private String id;
private String schoolBelongTo;
//......
}
public void showInfoOf(Person person){
System.out.println(person.getName());
//......
}
When using function "showInfoOf" ,if an object of Peron is used as the param,OK.However,if it is the type Student,I cannot get access to the field id and schoolBelongTo.
So I am confused ,how to ?
Actually, I want to know is this one of its(Interface oriented programming's or Supper class oriented programming's) disadvantages???
Two possible solutions:
You can programatically check the type in showInfoOf (Person), and use a cast to access & print the desired fields; or,
You can define a method on Person which will print/provide the desired info -- and either replace showPersonInfo() with that entirely, or call it into it. This is the more OO way.
Example:
abstract class Person {
private String name;
private int age;
private String gender;
public void printInfo() {
System.out.println( name);
}
}
class Student extends Person{
private String id;
private String schoolBelongTo;
#Override
public void printInfo() {
super.printInfo();
System.out.println( id);
System.out.println( schoolBelongTo);
}
}
public void showInfoOf (Person person){
person.printInfo();
}
In this example, all functionality has moved to Person.printInfo() and there is no real functionality remaining in showInfoOf (Person).
However in the real-world, you'd probably want move versatility in a Person.provideInfo() function -- perhaps returning a LinkedHashMap of fields & values (since unlabelled values on their own, are not great design).
The showInfoOf (Person) function could then handle formatting & printing the values to the specific requirement, leaving the Person.provideInfo() function general & multi-purpose.
in showInfoOf() you would have to check that person is of type Student, then cast it as a Student to get id or schoolBelongsTo

Empty constructors and setters on JPA Entites

I don't like the requirement on have at least one empty constructor and public setters on JPA entities. While I understand the issue on the EntityManager side, this invalidates class invariants.
Does anyone have a solution for this (design pattern or idiom level) ?
Thanks!
Igor
With JPA, the default constructor is required, however, you are not required to use setters. You can choose a property access strategy(field or method) based on where you place the annotations.
The following code will use direct field access and will work as a part of an entity without a setter:
#Column(name = DESCRIPTION)
private String description;
public String getDescription() { return description; }
Versus method access with a setter:
private String description;
#Column(name = DESCRIPTION)
public void setDescription(String description) {
this.description = description;
}
public String getDescription() { return description; }
In point of fact you should have both a no-args constructor and getter and setter methods. The requirements are indicated in section 2.1 of the spec.
The no-arg constructor requirement is found on page 17 in my copy :
The entity class must have a no-arg
constructor. The entity class may have
other constructors as well. The no-arg
constructor must be public or
protected.
Page 18 has the requirement for accessor methods :
The persistent state of an entity is
represented by instance variables,
which may correspond to Java- Beans
properties. An instance variable may
be directly accessed only from within
the methods of the entity by the
entity instance itself. Instance
variables must not be accessed by
clients of the entity. The state of
the entity is available to clients
only through the entity’s accessor
methods (getter/setter methods) or
other business methods. Instance
variables must be private, protected,
or package visibility.
Field vs. Property access indicates how the JPA provider interacts with your entity, not how the client application interacts with it. The client should always use get and set methods.
Some JPA providers are more lenient in these requirements and you may be able to make the constructor private (as suggested above) with a specific vendor. The application might not be portable though so you could be in for a surprise if you migrate in the future.
So I wouldn't recommend omitting the methods entirely. In order to resolve the problem I'd mark the public no-arg ctor as deprecated (put something in the javadoc about it being for JPA provider use only). The set methods can contain the logic you want to maintain your invariants.
It isn't ideal but it should prevent the wrong ctor from being used by accident (I'm assuming you have a ctor that sets the invariants).
OpenJPA can add a no-arg ctor as a part of enhancing your entities.
Just so we're clear, the requirement is mandated in the JPA spec. The previous answer says that you can make the no-arg ctor private, but that is not compliant with the spec(I see the link points to a Hibernate specific page). The spec states that an Entity must have a public or protected no-arg ctor.
-Rick
With DataNucleus you don't have to add a default constructor if you don't want to; it will be added automatically by bytecode enhancement. Also you could persist fields instead of properties, hence no need for public setters.
--Andy (DataNucleus)
Just make your constructor protected or private, so you preserve the class invariants!
public class Person {
private String firstName;
private String lastName;
public Person(String firstName, String lastName) {
setFirstName(firstName);
setLastName(lastName);
}
// private no-arg constructor for hibernate.
private Person() {
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
// private setters for hibernate
private void setFirstName(String nme) {
firstName = nme;
}
private void setLastName(String nme) {
lastName = nme;
}
}
see http://www.javalobby.org/java/forums/m91937279.html for details.
Yes, persist fields instead of properties, but on the not wanting a default constructor you're typically (unless the byte code enhancer does some trick) you're not going to get away from it.
You have to allow your jpa implementation to instantiate the class, so a public default constructor is mandatory.