Countless times I've read that public properties on a class are a major faux pas, but I fail to see why on data that doesn't get transformed going in/out.
example of something I would write
public class Employee
{
public String firstName;
public String lastName;
private int ssn = 0;
public boolean setSsn(String s)
{
//makes perfect sense why something like an ssn would use a getter setter
//some code to validate ssn
if(!validSsn(s)){
ssn = 0;
return false;
}
ssn = raw;
return true;
}
public String getSsn()
{
return ssn;
}
private boolean validSsn(String s)
{
//validation goes here
retrun val;
}
//I don't understand why I should make firstName private
// and then write
public void setFirstName(String s)
{
firstName = s;
}
public String getFirstName(String s)
{
return firstName;
}
}
please provide a scenario in which this would cause a problem and please be specific, not "because it causes issues when other people use your code" WHY? Why does it cause issues. Thanks in advance for constructive criticism and detailed answers :D
Imagine that your code has gone to production. I write a front-end that uses your Employee class that accesses firstName and lastName directly.
6 months go by, and now there's a new business requirement that you add validation to the name fields.
Where are you going to add it? You're going to have to make the fields private and add set methods, and this will force me and everyone else using your code to re-write our apps.
If you had encapsulated that data properly in the first place, all we'd have to do is recompile using the new version of the library with your code.
Related
I have this object. I want to access email attribute only in another class so I could check if the email is valid using assert in test directory. How do I access only email attribute from Person in another class to use it later to validate the email?
public class Person {
String name;
int year;
int month;
int day;
String email;
String phonenr;
public Person(String name, int year, int month, int day, String email, String phonenr) {
this.name = name;
this.year = year;
this.month = month;
this.day = day;
this.email = email;
this.phonenr = phonenr;
}
This is the class I want to access email attribute from:
public class PersonValidator {
public static boolean email(String email){
Person onePerson = new Person();
return false;
}
}
This is test class to test if email is valid:
class PersonRegisterTest {
#Test
void checkValidEmail() {
assertTrue(PersonValidator.email("adrianJames#oslomet.no"));
assertTrue(PersonValidator.email("example#example.com"));
assertTrue(PersonValidator.email("example.example#yahoo.com"));
}
Good practice in Java is to make all fields private, and create "getters and setters", i.e. functions to get and set values. For example:
public class Person {
private String email;
public void setEmail(String email) {
this.email = email;
}
public String getEmail() {
return this.email;
}
}
This way of doing things has several advantages:
If you you decide you want to change what values are allowed in a field, you can do that using the setter method. For example, if you want to impose a minimum length on emails, you can put if (email.length() < 10) return; in your method to prevent emails shorter than 10 characters
If you decide you want to retrieve emails over the internet or some other way, you do not have to change your code.
Many tools and frameworks expect Java objects to be in this format. Notable examples include Jackson for JSON serialization, and Spring/Spring Boot, for web application development, as well as many many more.
P.S. if you are sick of writing getters and setters:
Check if your IDE has a way of automatically generating them (most of them do)
Look into a library called Lombok, it provides the #Data annotation which can generate these methods automatically at compile time. (https://projectlombok.org/)
Its better to keep your data members/variables inside the class Person as private, and you can add getters and setters method, which you can access from outside the class and using which you can access the data members/variables of your class.
You can do something like:
public class Person {
String name;
int year;
int month;
int day;
String email;
String phonenr;
public Person(String name, int year, int month, int day, String email, String phonenr) {
this.name = name;
this.year = year;
this.month = month;
this.day = day;
this.email = email;
this.phonenr = phonenr;
}
public String getEmail()
{
return this.email;
}
}
For your case I'd make the the Person.email as package private - so it can stay as it is now, and put the PersonValidator class in the same package where you have Person class.
That would make you able to pass whole Person object to validator method like:
public static boolean email(Person person) {
String email = person.email;
return false;
}
But this is very isolated and not well designed. Also, there are plenty of ways being able to call methods from one class in another. E.g. create isEmailValid method in Person class and where you could directly use the email variable when calling PersonValidator.email(email).
Many of those uncommon approaches are most likely to invalidate SOLID, clean code and some other principles (like too tight coupled classes).
Like other answers stated it's much better to keep object fields well encapsulated (i.e. using private access modifier) and later access them with get/set methods.
Then regardless if you first create Person object (e.g. parse the Person list from file and then validate if they have correct email or get request params from smth like HTTP request for new person) you could call PersonValidator.email(personObject.getEmail() or PersonValidator.email(emailParam).
The package of keeping the PersonValidator class depends only on the convention of your project.
Having the "getters/setters" methods in an object, i.e. getEmail() setEmail(String email) getName() setName(String name) is common and good convention to keep your code well designed.
It's a good practice to implement such when applicable.
Just for further reading:
Having public getter method for email field in Person class shouldn't invalidate any coding standards (not even YAGNI-You Ain't Gonna Need It), because there could be plenty of future usages (e.g. displaying it for person/client using your program).
The mentioned encapsulation term in short words, it's about keeping inner implementation of an object inside it - i.e. not making it visible by other classes.
There're situations where an object modifies its inner fields by its methods, which are not the "setters" ones, but the current field value should always be accessed with getField() method.
"Don't overdesign, but keep future growth in mind" (i'm probably not citing anyone :P ).
The further use case could be to validate more field members of a Person object, which could result in having:
class PersonValidator {
public static boolean validatePerson(Person person) {
return validEmail(person.getEmail()) &&
//other validators when needed &&
validPhone(person.getPhonenr());
}
//very hard to validate
//most likely if has just a numbers (replacing "-" before)
//and only a length check (at least 3 without country code)
public static boolean validPhone(String phone) {
return false; //
}
public static boolean validEmail(String email) {
return false;
}
}
Again, as stated in other answers, it's much better to try searching for existing validator libraries, than writing new one - REALLY, REALLY.
There're different lenths and formats of phone numbers (mostly dependend on country), check this out.
The shortest valid email address could be a#a, or " "#a, but those are local email addresses. Check this wiki or this github gist to see some strange valid examples.
And the name, it depends on country laws and they are some which probably could accept the first names such as "A" or "Ben11" and the weirdest surname in Poland is "Cyps Albo Zyps".
First add getters to you Person object/class and make the internal state of the object only accessible via the getters:
public class Person {
private final long id;
private final String name;
private final String email;
public Person(long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
public String getEmail() {
return email;
}
// Other 2 getters
}
Then build a validator that can validate email address format, or better use a library (like https://commons.apache.org/proper/commons-validator/) for that:
public final class EmailValidator {
private EmailValidator() { }
public static boolean isValidEmail(String email) {
// check if valid email and return true or fale
}
}
Call the validator by getting the email address from new person by calling the getter. The validator shouldn't have any knowlegde of the Person object.
public static void main(String[] args) {
Person newPerson = new Person(1L, "test user", "valid#hotmail.com");
if (EmailValidator.isValidEmail(newPerson.getEmail())) {
System.out.println("Valid email address");
} else {
System.out.println("Invalid email address");
}
}
Took me a bit to figure this out but Im just wondering if there is a cleaner way to do this
this is the gist of my main
public class Main {
private static Bank Chase = new Bank();
//This is the function in main to add a transaction to a specified customer of a branch
public static void addTransaction() {
System.out.println("Enter the name of the branch");
String branch = scanner.nextLine();
System.out.println("Enter the name of the person");
String name = scanner.nextLine();
System.out.println("Enter the amount you would like to add");
double amount = scanner.nextDouble();
scanner.nextLine();
Chase.getBranchList().get(Chase.branchIndex(branch)).getCustomerList().get(Chase.getBranchList().get(Chase.branchIndex(branch)).customerIndex(name)).addTransaction(amount);
}
}
This last line is really long and confusing to others this is what it does
//gets the branchlist -> gets the specified branch -> gets the customerlist -> finds the specified customer -> adds transaction
these are the other relevant parts of the classes the function references
public class Bank {
private ArrayList<Branch> branchList = new ArrayList<Branch>();
public ArrayList<Branch> getBranchList() {
return branchList;
}
public int branchIndex(String name){
for(Branch branch: branchList){
if(branch.getName().equals(name)){
return branchList.indexOf(branch);
}
}
return -1;
}
}
public class Branch {
private String branchName;
private ArrayList<Customer> customerList;
public ArrayList<Customer> getCustomerList() {
return customerList;
}
public int customerIndex(String name){
for(Customer customer: customerList){
if(customer.getName().equals(name)){
return customerList.indexOf(customer);
}
}
return -1;
}
public class Customer {
private String customerName;
private ArrayList<Double> transactions = new ArrayList<Double>();
public Customer(String customerName, double amount) {
this.customerName = customerName;
this.transactions = new ArrayList<Double>();
transactions.add(amount);
}
public String getName() {
return customerName;
}
public void addTransaction(double transaction){
transactions.add(transaction);
}
}
So is there any more readable way of accessing these elements that are in object ArrayLists? I think the last line in addTransaction() looks a bit redundant.
Rather than one long line you could
a) split the code into multiple lines
Chase.getBranchList().get(Chase.branchIndex(branch))
.getCustomerList()
.get(Chase.getBranchList()
.get(Chase.branchIndex(branch))
.customerIndex(name))
.addTransaction(amount);
b) stored the returned values of each get into a local variable, especially the code that it re-calling the same methods e.g. Chase.branchIndex(branch) and Chase.getBranchList()
At the moment you are assuming unique customer/branch names, and then cycling through your array list to find the customer by name. This assumption is fine, if it's a valid assumption but could mean that there are more optimal solutions. I would recommend a refactor of your code to utilise a java hash map:
https://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
Basically, this will mean that you can access the customer/bank directly by name and will simplify your code greatly! It will also have performance benefits.
For your scenario this refactor would look similar to this:
public class Branch
{
private HashMap<String, Customer> _customers;
private String _branchName;
public Branch(String branchName)
{
_branchName = branchName;
_customers = new HashMap<String, Customer>();
}
public Customer getCustomer(String customerName)
{
return _customers.get(customerName);
}
}
If you follow the same for Bank, you should be able to access a Customer and add a transaction as follows:
Chase.getBranch(branch).getCustomer(name).addTransaction(transaction);
Let me know if you need help converting Bank :)
You are on the right track, but you've got some minor design flaws.
Step 1: Add a method called getBranchByName(String branchName) to your Bank class that returns a Branch object and get rid of your branchIndex() method:
public Branch getBranchByName(String branchName) {
return branchList.stream()
.filter(branch -> branch.getBranchName().equals(branchName))
.findAny()
.get();
}
Step 2: Add a method called getCustomerByName(String name) to your Customer class that returns a Customer object and get rid of your customerIndex() method:
public Customer getCustomerByName(String name) {
return customerList.stream()
.filter(customer -> customer.getCustomerName().equals(name))
.findAny()
.get();
}
Step 3: Now, method call in your main() method becomes more compact, simple and easy to read:
Chase.getBranchByName(branchName).getCustomerByName(customerName).addTransaction(amount);
Note: I've used Java 8 streams as you can observe. If you are not allowed to use Java 8 streams, you can just stick with classic imperative style of programming by writing for() loops as you have done earlier. As a quick example, if you want to write getBranchByName(String branchName) in old fashioned Java 7 style, your loop looks like this:
for(Branch branch : branchList) {
if(branch.getBranchName().equals(branchName)){
return branch;
}
}
This question already has answers here:
Invoking all setters within a class using reflection
(4 answers)
Closed 5 years ago.
I have a POJO object and a collection of appropriate data.
import java.util.ArrayList;
import java.util.List;
public class TestPojo {
private String name;
private String number;
private String id;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public static void main(String[] args) {
TestPojo test = new TestPojo();
List<String> sampleData = new ArrayList<>();
sampleData.add("Bob");
sampleData.add("641-613-623");
sampleData.add("id-1451");
sampleData.add("Male");
test.setName(sampleData.get(0));
test.setNumber(sampleData.get(1));
test.setId(sampleData.get(2));
test.setSex(sampleData.get(3));
}
}
My question is how can i fill my POJO object with data in a loop? Is it posible to iterate all object setters and set data from List in appropriate places? I know that reflection can help in this case.
Here is an simple example to call setters via reflection (which needs to be adjusted):
[if this is a good approach, is another question. But to answer your question:]
public static void main(String[] args) throws Exception
{
//this is only to demonstrate java reflection:
Method[] publicMethods = TestPojo.class.getMethods(); //get all public methods
TestPojo testObj = TestPojo.class.newInstance(); //when you have a default ctor (otherwise get constructors here)
for (Method aMethod : publicMethods) //iterate over methods
{
//check name and parameter-count (mabye needs some more checks...paramter types can also be checked...)
if (aMethod.getName().startsWith("set") && aMethod.getParameterCount() == 1)
{
Object[] parms = new Object[]{"test"}; //only one parm (can be multiple params)
aMethod.invoke(testObj, parms); //call setter-method here
}
}
}
You can also save all setter-methods in an list/set for later re-use...
But as others already said, you have to be careful by doing so (using reflection)!
Cheers!
You can't easily - and you shouldn't.
You see, your POJO class offers some setters. All of them have a distinct meaning. Your first mistake is that all of these fields are strings in your model:
gender is not a string. It would rather be an enum.
"number" is not a string. It should rather be int/long/double (whatever the idea behind that property is)
In other words: you premise that "input" data is represented as array/list is already flawed.
The code you have written provides almost no helpful abstractions. So - instead of worrying how to call these setter methods in some loop context - you should rather step back and improve your model.
And hint: if this is really about populating POJO objects from string input - then get your string into JSON format, and use tools such as gson or jackson to do that (reflection based) mapping for you.
"Iterating over methods" seems pretty much of a wrong idea in OO programming. You could simply add a constructor to your class setting all of your attributes, and then just call that constructor in a loop as desired to create new objects with data you desire.
In your class define:
public TestPojo(String name, String number, String id, String sex){
this.name = name;
this.number = number;
this.id = id;
this.sex = sex;
}
Also using a List makes no much sense here. I'd recommend using a HashMap to then iterate over it in a for loop making proper calls of the above constructor.
I have myArrayList that already have values in it. For this example lets say there are only two elements in it(firstName and lastName). I need to get values from myArrayList and compare them to String and if it matches then get value from the bean and put it into map:
Map<String,String> myMap;
for(String element: myArrayList){
if(element.equalsIgnoreCase("firstName")){
myMap.put("firstName", bean.getFirstName());
}else if(element.equalsIgnoreCase("lastName")){
myMap.put("lastName", bean.getLastName());
}
}
The problem is when you have thirty-forty elements in myArrayList you will have performance issues(I assume), and it just doesn't feel right.
I tried this:
String name = null;
String value = null;
for(int i = 0; i < myArrayList.size(); i++){
name = myArrayList.get(i);
value = bean.get(name);
myMap.put(name, value);
}
But the line "value = bean.get(name);" is saying that method get(String) is undefined in bean class, indeed we don't have such method in bean class, it has only standard getter and setter methods:
public class Bean implements Serializable {
private String firstName;
private String lastName;
public String getFirstName(){
return firstName;
}
public void setFirstName(String firstName){
this.firstName = firstName;
}
public String getLastName(){
return lastName;
}
public void setLastName(String lastName){
this.lastName = lastName;
}
}
Now I am thinking how I could come up with some design pattern that optimizes my logic and doesn't affect performance of the code. Please feel free to ask questions, I will edit if you need more info. Any help is greatly appreciated. Thanks.
Edit: shmosel's answer was pretty good for me, thank you all for your help! Cheers!
#HankD and #Natalia have offered some valid solutions, but another option I don't see mentioned is refactoring Bean to support a get(String) method:
public class Bean implements Serializable {
private Map<String, String> properties = new HashMap<>();
public String get(String property) {
return properties.get(property);
}
public void set(String property, String value) {
properties.put(property, value);
}
public String getFirstName(){
return get("firstName");
}
public void setFirstName(String firstName){
set("firstName", firstName);
}
public String getLastName(){
return get("lastName");
}
public void setLastName(String lastName){
set("lastName", lastName);
}
}
You can try to use reflection see javaDoc
However, I'll not recomend to use it, until it is really required. Possible, you should refactor you code to avoid having list of fields, you need to get.
If you decide you use reflection, there is ReflectionUtils in springframework
Your get(String) method is a really good idea, it just needs to be done right. Here's how I would do it, it's very similar to what you did outside of the Bean, but it allows separation of concerns, which is a good thing.
public String get(String field) {
switch(field.toLowerCase()) {
case "firstname":
return firstName;
case "lastname":
return lastName;
default:
throw new IllegalArgumentException(field + " is an invalid field name.");
}
}
I'm using a switch statement here because the Java Docs note that:
The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.
If you can't change your Bean class, then you should at least use this logic inside your loop instead of your current logic simply for the better speed of switch statements and calling toLowerCase() once instead of using equalsIgnoreCase() multiple times.
This seems like a strange way to do things. As 4castle points out, a few elements are not likely to cause performance problems in itself. Are you experiencing performance problems?
If it was me, I'd do something like this:
public static final String lastNameValue = "lastname";
for(String element: myArrayList){
if(element != null) element = element.toLowerCase();
if(lastNameValue.equals(element){
myMap.put("lastName", bean.getLastName());
} ....
}
The constants prevent the construction of a new String each time this method is called. You are not checking for a null element. Doing the toLowerCase() once is more efficient than doing it a number of times.
So, just as an example, let's say we have an abstract class called Question, that question contains a lot of strings, one for the question itself, one for the answer and two responses posted to the user, if he got the question right / wrong.
public abstract class Question {
private final String question;
private final String answer;
private final String answerCorrect;
private final String answerWrong;
}
My question basically is, what would be a common way to initialize all of the strings? So far I've made up 2 versions on how to do it, they have their up- and downsides and I wanted to know, if there was some kind of "best coding practice" for this.
Version A
Initialize everything in the constructor.
public abstract class Question {
//...
public Question(String question, String answer, String answerCorrect, String answerWrong) {
this.question = question;
this.answer = answer;
this.answerCorrect = answerCorrect;
this.answerWrong = answerWrong;
}
}
This seems pretty convenient, the only problem I have with this is that users will not be sure, in which order the strings have to be.
public class ExampleClass extends Question {
public ExampleClass() {
super("I think, that's the answer", "and that's the question", "answer wrong?", "answer right?");
}
}
Version B
Don't initialize instantly and wait for the user to do it.
public abstract class Question {
//...
public Question() {
this.question = "";
this.answer = "";
this.answerCorrect = "";
this.answerWrong = "";
}
public void setQuestion(String question) {
this.question = question;
}
//...
}
This makes it easier to initialize variables, but the Strings can't be final anymore and it's not guaranteed that the user will initialize all of them.
I've also thought about letting the child-class implement abstract methods that are called in the constructor of Question to initialize all the strings and to keep them final, but that version seemed a little too strange to me.
Are there other / better ways to do it? What version should I prefer?
Thanks in advance for your support.
Version A is the way to go. You're right, though, if you do not tell your users (the other developers I'm assuming) which parameter is which, there is no way for them to know where to type what.
This is where Javadoc comes in handy.
Here's an example:
/**
* Create a new instance of Question given the following parameters:
*
* #param question This is the question
* #param answer This is the answer
* #param answerCorrect Whenever someone guesses correct, print this
* #param answerWrong Whenever someone guesses wrong, print this
*/
public Question(String question, String answer, String answerCorrect, String answerWrong) {
this.question = question;
this.answer = answer;
this.answerCorrect = answerCorrect;
this.answerWrong = answerWrong;
}
This might be overkill, but I believe you could use a builder here...
public class Question
{
private final String question;
private final String answer;
private final String answerCorrect;
private final String answerWrong;
Question(QuestionBuilder builder) {
this.question = builder.question;
this.answer = builder.answer;
this.answerCorrect = builder.answerCorrect;
this.answerWrong = builder.answerWrong;
}
// public getters omitted to shorten answer
#Override
public String toString(){
return String.format("question: '%s', answer: '%s', answerCorrect: '%s', answerWrong: '%s'", question, answer, answerCorrect, answerWrong);
}
public static void main(String[] args) {
QuestionBuilder qb = new QuestionBuilder();
qb = qb.question("This is the question").answer("This is the answer").answerCorrect("Correct answer").answerWrong("Wrong Answer");
Question question = new Question(qb);
System.out.println(question);
}
public static class QuestionBuilder{
private String question;
private String answer;
private String answerCorrect;
private String answerWrong;
public QuestionBuilder question(String question) {
this.question = question;
return this;
}
public QuestionBuilder answer(String answer) {
this.answer = answer;
return this;
}
public QuestionBuilder answerCorrect(String answerCorrect) {
this.answerCorrect = answerCorrect;
return this;
}
public QuestionBuilder answerWrong(String answerWrong) {
this.answerWrong = answerWrong;
return this;
}
}
}
Gives the output
question: 'This is the question', answer: 'This is the answer', answerCorrect: 'Correct answer', answerWrong: 'Wrong Answer'
Note: I realize the original question was in reference to an abstract class. I used a concrete class so I could give a working example, although the solution can be adapted for use with an abstract class.
Instead of thinking of the attributes (such as question) as just variables, think of the restrictions on their values that must be obeyed for the classes to behave correctly. Can they be null? Can they be empty? Now design your methods and the constructor so it is impossible for those restrictions to be broken. You might find that the only way you can do this is to set initial values in the constructor (your version A). You might have to add pre-condition checks to your constructor and setter methods, which check the values given a throw a suitable exception (NullPointerException or IllegalArgumentException) if the values passed to them would result in the restrictions being broken.
Also, consider whether it really makes sense to change the value of an attribute after the object is constructed. If not, then the attribute should not a setter, making your version B impossible.