Say I have a class like so:
public class ManyFields {
public Object1 object1;
public Object2 object2;
public Object3 object3;
// etc.
}
I want to make sure these fields are not null upon trying to do anything with a ManyFieldsobject. So perhaps I'd have a validation method like so:
public Object ensureNotNull(Object o) {
if (o.object1 != null) o.object1 = new Object1();
// and so on and so forth
return o;
}
Is there a better, but also performant, approach here? Rather than checking if each field is null individually? I did explore reflection like so:
for (Field f : fields) {
try {
if (f.get(c) == null) {
f.set(c, f.getType().newInstance());
}
} catch (Exception e) {
e.printStackTrace();
}
}
However, I've read this reflection isn't the most performant-friendly approach to initializing fields. Any suggestions would help? Thanks.
Reflection is the worst and slowest way to do almost anything.
What you want is to encapsulate your fields: make them private, so your class has total control over how they get changed.
If there aren’t too many of them, you can make them all final and have the constructor set them:
public class Person {
private final String firstName;
private final String lastName;
public Person(String firstName,
String lastName) {
this.firstName = Objects.requireNonNull(firstName,
"First name cannot be null.");
this.lastName = Objects.requireNonNull(lastName,
"Last name cannot be null.");
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
With this approach, it is completely impossible for outside code to set the fields to null values.¹ If a caller passes null, the constructor never completes, so there can never be an existing instance of Person which has a field with a null value. Which means you can make a written guarantee that callers will never have to check if the values are null:
/**
* Returns this user's first name. This value will never be null.
*/
public String getFirstName() {
return firstName;
}
/**
* Returns this user's last name. This value will never be null.
*/
public String getLastName() {
return lastName;
}
You can even take it a step farther, and validate the values beyond just checking for null:
public Person(String firstName,
String lastName) {
this.firstName = Objects.requireNonNull(firstName,
"First name cannot be null.");
this.lastName = Objects.requireNonNull(lastName,
"Last name cannot be null.");
if (firstName.isBlank()) {
throw new IllegalArgumentException("First name cannot be blank.");
}
if (lastName.isBlank()) {
throw new IllegalArgumentException("Last name cannot be blank.");
}
}
If you have many fields, you can just use get-methods and set-methods instead of setting everything in the constructor. In that case, it is useful to initialize each field to a non-null value, so the class is able to make the same non-null guarantee as with the constructor approach:
public class Person {
private String firstName = "(unknown)";
private String lastName = "(unknown)";
private String socialSecurityNumber = "000-00-0000";
private LocalDate dateOfBirth = LocalDate.MAX;
public String getFirstName() {
return firstName;
}
public String setFirstName(String name) {
this.firstName = Objects.requireNonNull(name,
"Name cannot be null.");
}
public String getLastName() {
return lastName;
}
public String setLastName(String name) {
this.lastName = Objects.requireNonNull(name,
"Name cannot be null.");
}
public String getSocialSecurityNumber() {
return socialSecurityNumber;
}
public void setSocialSecuityNumber(String num) {
Objects.requireNonNull(num, "Argument cannot be null.");
if (!num.matches("\\d{3}-\\d{2}-\\d{4}")) {
throw new IllegalArgumentException(
"Argument must be in the format nnn-nn-nnnn.");
}
this.socialSecurityNumber = num;
}
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(LocalDate date) {
Objects.requireNonNull(date, "Date cannot be null.");
int age = date.getYear() - LocalDate.now().getYear();
if (age > 150) {
throw new IllegalArgumentException(
"Invalid date: no one is more than 150 years old.");
}
if (age < 0) {
throw new IllegalArgumentException(
"Invalid date: cannot be born in the future.");
}
this.dateOfBirth = date;
}
}
1. It is technically possible for outside code to use reflection’s setAccessible method to hack the private members of a class that is not in a module, unless a SecurityManager is installed. However, people who hack into things in this manner should expect the class to break in unexpected ways, as they are essentially “voiding the warranty.” Meaning, it’s bad and no one should do it, and in fact later versions of Java won’t let them do it.
Related
i have a class called Student has two variables:
String lastName;
String firstName;
public Student(String lastName,String firstName){
this.lastName = lastName;
this.firstName = firstName;
}
public Student(String lastName){
this.lastName = lastName;
}
#Override
public int hashCode() {
return super.hashCode();
}
#Override
public boolean equals(Object obj) {
String getLastName = ((Student) obj).getLastName();
return lastName.equalsIgnoreCase(getLastName);
}
in the Main class i have created an ArrayList
private static ArrayList<Student> listOfStudents = new ArrayList<>();
and i have created this method to get the students from ArrayList
public void findStudent(String lastName){
for (int i=0;i<listOfStudents.size();i++){
if (listOfStudents.get(i).equals(new Student(lastName))){
System.out.println(listOfStudents.get(i));
}
}
}
I have overridden equals() to check by last name.
Now everything is well, but when i add duplicate last name like below:
listOfStudents.add(new Student("Tamoussat","Abdelillah"));
listOfStudents.add(new Student("Tamoussat","Fatima"));
listOfStudents.add(new Student("Soussi","Ahlam"));
I get only the first element, i want the way of how to get more than one element if they have the same last name?
Best Regards
public class Student {
private String lastName;
private String firstName;
public Student(String lastName, String firstName) {
this.lastName = lastName;
this.firstName = firstName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
#Override
public String toString() {
return firstName + " " + lastName;
}
}
You can filter your list with the java stream api:
public static void findStudent(List<Student> listOfStudents, String lastName){
listOfStudents.stream().filter(s -> s.getLastName().equals(lastName)).forEach(student -> {
// for each student with the lastName
System.out.println(student);
});
}
The mistake will be in your equals() method, as running the code above works as expected with my own implementation of equals() which just compares lastName of this with the object argument.
But still, try to improve this method
public void
findStudent(String lastName)
{
for (Student student : listOfStudents)
{
if (student.lastName().equals(lastName))
{
System.out.println(student);
}
}
}
or any other version like #CodingSamples 's
Your equals() and hashCode() should work, despite they are not canonical. I would write it in a more better way:
#Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Student student = (Student) o;
return Objects.equals(lastName, student.lastName);
}
#Override
public int hashCode() {
return Objects.hash(lastName);
}
But this does not explain why your code returns only one item from the array. You can try my implementation or post more precisely code you execute with data you use and the result of execution.
Thanks for helping, finally i found the problem was in my code, i have a method to add students to array, in that method i used
if(!listOfStudents.contains(new Student(lastName,firstName)){}
So the problem was in other method, when it found duplicate element it doesn't add it to the ArrayList, when i deleted this if statement from that method, code worked good, Best Regard
In my program I'm trying to compare names by last name, and if those are the same then compare by using the first name. However, I can't quite figure out how to compare the strings.
Can someone help me out with this?
public class Student implements IComparable
{
String firstName;
String lastName;
int score;
public Student()
{
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getFirstName()
{
return firstName;
}
public void getLastName(String lastName)
{
this.lastName = lastName;
}
public String getLastName()
{
return lastName;
}
public void getScore(int score)
{
this.score = score;
}
public int getScore()
{
return score;
}
#Override
public int compareTo(Object o)
{
//Compares Student objects by last name. If the last names are the same
//it compares by first name.
Student s = (Student) o;
if (this.getLastName().toUpperCase() < s.getLastName().toUpperCase())
return -1;
else if (this.getLastName().toUpperCase() > s.getLastName().toUpperCase())
return 1;
else
{
if(this.getFirstName().toUpperCase( < s.getFirstName().toUpperCase()
return -1;
else if (this.getFirstName().toUpperCase( > s.getFirstName().toUpperCase()
return 1;
else
return 0;
}
}
}
Don't make things more complicated:
String class already provides compareToIgnoreCase method
value returned by compare methods of String is already good to be directly returned
Basically the same functionality could be expressed with:
int compare = getLastName().compareToIgnoreCase(o.getLastName());
return compare == 0 ? getFirstName().compareToIgnoreCase(o.getFirstName()) : compare;
Mind that you need to check that o instanceof Student if you have an Object argument.
I don't get why you are using a custom IComparable interface, which sounds much like the one provided in C#, since Java provides Comparable<T> which is generic and doesn't require checking for the runtime type of the argument (since it's not Object anymore but T).
I was given an empty class and instructed to fill it with instance variables and methods with bodies, which set or get values from the instance variables. Then I am to open another java file where code creating a new Contact object was provided and instructed to add additional code that uses that object to test all the methods created in the Contact class.
I am using the program Eclipse for coding/testing. The only things are correct are my method headers. When testing with what I have written (below) I get a null, null response (that is when I remove what wrote about the setPhoneNumber. When it's included I get a worse looking error
Exception in thread "main" java.lang.Error: Unresolved compilation
problem: phoneNumber cannot be resolved to a variable.)
Any assistance on getting the get/set methods to work will be much appreciated. I am entirely new to any programming and have looked at about dozens of examples and have tried many variations with no success.
public class Contact
{
//Instance Variables
private String firstName;
private String lastName;
private int phoneNumber;
public String getFirstName() //method to retrieve first name
{
return this.firstName;
}
public void setFirstName(String firstName) //method to set the first name
{
this.firstName = firstName;
}
public String getLastName()
{
return lastName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
public int getPhoneNumber()
{
return this.phoneNumber;
}
public void setPhoneNumber(int phoneNumber)
{
this.phoneNumber = phoneNumber;
}
public void call()
{
System.out.printf(getFirstName(), getLastName(), getPhoneNumber());
}
}
Here is the other java file used to test the Contact class above:
public class ContactTestDriver
{
public static void main(String[] args)
{
Contact contact = new Contact();
//all above was given; below are my code additions
String myContact;
myContact = contact.getFirstName();
contact.setFirstName();
System.out.println(myContact);
myContact = contact.getLastName();
contact.setLastName();
System.out.println(myContact);
int myNumber;
myNumber = contact.getPhoneNumber();
contact.setPhoneNumber(phoneNumber);
System.out.println(myNumber);
}
}
After some helpful comments I have made changes but still no success. My error is "myContact cannot be resolved to a variable"
Here is the revised code:
public class Contact
{
//Instance Variables
private String firstName;
private String lastName;
private int phoneNumber;
public void setFirstName(String firstName) //method to set the first name
{
this.firstName = firstName;
}
public String getFirstName() //method to retrieve first name
{
return this.firstName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
public String getLastName()
{
return lastName;
}
public void setPhoneNumber(int phoneNumber)
{
this.phoneNumber = phoneNumber;
}
public int getPhoneNumber()
{
return this.phoneNumber;
}
public void call()
{
System.out.printf("Who to call ", getFirstName(), getLastName(), getPhoneNumber());
}
}
And this is the code to test the Contact class:
public class ContactTestDriver
{
public static void main(String[] args)
{
Contact contact = new Contact();
//all above was given; below are my code additions
String firstName = "a"; //define first name
contact.setFirstName(firstName); //then set it
myContact = contact.getFirstName(); //then get it
System.out.println(myContact);
String lastName;
contact.setLastName(lastName);
myContact = contact.getLastName();
System.out.println(myContact);
int myNumber = 123;
contact.setPhoneNumber(phonNumber);
myNumber = contact.getPhoneNumer();
System.out.println(myNumber);
}
}
When you call contact.setFirstName(); which take String parameter you didn't pass the first name
public void setFirstName(String firstName){...}
and also with contact.setLastName(); : this also needs to send parameter
like this :
String lastName="//last name";
contact.setLastName(lastName);
and you need to know that we use the setter method before getter method
so first set the names then get it
String firstName = "a";////define the first name
contact.setFirstName(firstName); //// then set it
myContact = contact.getFirstName(); /// then get it
System.out.println(myContact);
String lastName = "b";
contact.setLastName(lastName);
myContact = contact.getLastName();
System.out.println(myContact);
int myNumber = 123;
contact.setPhoneNumber(myNumber);
myNumber = contact.getPhoneNumber();
System.out.println(myNumber);
This is what you have:
myContact = contact.getFirstName();
contact.setFirstName();
First you are getting, then you are setting. Reversing them makes more sense:
contact.setFirstName();
myContact = contact.getFirstName();
Moreover, set methods like setFirstName should receive a String parameter:
contact.setFirstName("Johny");
You can use Eclipse debugging tool.Run you java class in debug mode and add break points where ever you need to analyze data.
getter is just a method getting a field and setter is setting a new field.
you must set some values first i.e
int myNumber;
myNumber = contact.getPhoneNumber();
contact.setPhoneNumber(phoneNumber);
System.out.println(myNumber);
int myNumber= "983445556"; // here you are creating an instance
or you can do it by
contact.setPhoneNumber(32435435345);
similarly for rest cases
Here phoneNumber is not a defined variable in your main method
int myNumber;
myNumber = contact.getPhoneNumber();
contact.setPhoneNumber(phoneNumber);
it should be defined.
so I have 2 files called Employee.java and Write.java (These two are within the same package). Say within Employee.java I have
public class Employee {
private String firstName = "test";
private String lastName = "ing";
public Employee(String first, String last) {
firstName = first;
lastName = last;
}
public Employee(Employee copy) {
firstName = copy.firstName;
lastName = copy.lastName;
}
}
Then within my Write.java I want to create an object of type Employee called temp. Like
public void obtainInfo(Employee temp) {
String firstName = temp.firstName;
String lastName = temp.lastName;
}
However I get an error that tells me that it cannot find the symbol in the line that is.
public void obtainInfo(Employee temp) {
I was just wondering where I went wrong that I can't create an object within different files. Despite them being in the same package I can't access them?
I want to be able to incorporate this later on to help me build a text file from reading an array list, but I figured to first start with reading a single line from an object.
It sounds to me that you are trying to set something up so that you can make a copy of an Employee and be able to write the properties to a text file. This won't write to a text file but I think it may clear some things up for you.
public class Employee {
private String firstName;
private String lastName;
public Employee(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public Employee(Employee copy) {
firstName = copy.firstName;
lastName = copy.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;
}
#Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("Employee");
sb.append("{firstName='").append(firstName).append('\'');
sb.append(", lastName='").append(lastName).append('\'');
sb.append('}');
return sb.toString();
}
}
TestClass.java
public class TestClass {
public static void main(String[] args){
//First we have to have one to copy
Employee emp = new Employee("Joe", "Dirt");
//Now we have a copy
Employee emp2 = new Employee(emp);
//Calls the Employee.toString() method and sends it to System.out
System.out.println("Employee 1 : " + emp);
System.out.println("Copy of Employee 1 : " + emp2);
}
}
Make sure that Write.java's class has the same level of access as Employee (IE: Public). If this is not the issue, I would show the code from Write.java specifically as that is most likely where the problem is coming from.
I have the following class :
public class Project {
private int id;
private String name;
public Project(int id, String name) {
if(name == null ){
throw new NullPointerException("Name can't be null");
}
if(id == 0 ){
throw new IllegalArgumentException("id can't be zero");
}
this.name = name;
this.id = id;
}
private Project(){}
public int getId() {
return id;
}
public void setId(int id) {
if(id == 0 ){
throw new IllegalArgumentException("id can't be zero");
}
this.id = id;
}
public String getName()
return name;
}
public void setName(String name) {
if(name == null ){
throw new NullPointerException("Name can't be null");
}
this.name = name;
}
}
If you noticed that setName and setId share the same validation for its fields with the constructor. Is this redundant code that could cause issues in the future ( for example if somebody edit the the setter to allow 0 for the id and prevent -1 instead but didn't change the constructor) ? . Should I use a private method to do the check and share it between the constructor and the setter which seems too much if there's a lot of fields.
Note: This is why im not using the setters in the constructor. https://stackoverflow.com/a/4893604/302707
Here is the revised code:
public class Project {
private int id;
private String name;
public Project(int id, String name, Date creationDate, int fps, List<String> frames) {
checkId(id);
checkName(name);
//Insted of lines above you can call setters too.
this.name = name;
this.id = id;
}
private Project(){}
public int getId() {
return id;
}
public void setId(int id) {
checkId(id);
this.id = id;
}
public String getName()
return name;
}
public void setName(String name) {
checkName(name);
this.name = name;
}
private void checkId(int id){
if(id == 0 ){
throw new IllegalArgumentException("id can't be zero");
}
}
private void checkName(String name){
if(name == null ){
throw new NullPointerException("Name can't be null");
}
}
}
I recommend that you should define one method per field as isValid() and then call the same method in you setter as well as Constructor.
I would say yes. Rather than that, just call the setters from your constructor:
public Project(int id, String name, Date creationDate, int fps, List<String> frames) {
setName(name);
setId(id);
// other stuff with creationDate, fps and frames?
}
Also, you shouldn't check for a null name in getName -- do it in setName. Otherwise, bugs are going to be hard to track down -- you want to catch the invalid name as soon as it comes in, not when it's used (which may be much later).
if you make Project immutable, it will eliminate the redundant code. but for now, i think explicitly throwing exceptions in both the constructor and mutator methods is fine.
and i would not call the mutator methods within the constructor for many reasons, including this. also, i would remove the validation code in the accessor method...it's not necessary.