There’s a lot of frustration going on with the new release of Ruby on Rails 5.1. I’m not going to go into the specifics of all the drama here in this blog post, but I’m planning on writing more about it in the future, so if you want to know more, you can find me on Twitter. Well, it’s been a while since I’ve been around. I’ve been toiling away, writing code, refining the search engine, and working on new projects. A blog post about polymorphism , static classes and overriding methods.
Use of access control
The following table shows the access to the field or method marked by the access modifier in the left column. The access modifier keywords in this table are private, protected and public. If the keyword is missing, the default access modifier is applied. private : Provides the best control over access to fields and methods. With private, a data field or method is only accessible in one Java class. standard : Also called packet-level access. By default, a data field or method is accessible within a single class or package. The standard class cannot be underclassified outside its package. protected : Allows access within a package and subclass. Fields and methods that use protected are considered subclass friendly. Protected access applies to subclasses that are in a different package than the class to which the protected function belongs. As a result, protected fields or methods are actually more accessible than those with standard access control. the public: Provides the greatest access to fields and methods and makes them available everywhere: in the class, in the package, in subclasses and in any other class.
Secure access control: Example
Two classes in two packages are presented below. The class Foo is in the demo package and declares a data field called result with a protected access modifier. 1 package demo ; 2 public class Foo { 3 protected int result = 20 ; 4 int num = 25 ; 5 } 1 Package test; 2 import demo.Foo; 3 public class Bar extends Foo { 4 private int sum = 10; 5 public void reportSum () { 6 sum += result; 7 sum +=num; 8 } 9 } The class Bar, which Foo extends, has a method reportSum that adds the result value to the total. The method then tries to add the value of num to sum. The num field is declared with a default modifier, which causes a compile-time error. Why? Answer: The result field, declared as a protected field, is available to all subclasses, even those in another package. The num field is declared as the default access and is only available for classes and subclasses declared in the same package.
Access control: Good practice
A good practice when working with fields is to make them as inaccessible as possible and to ensure that there is a clear intention to use the fields with the methods. 1 package demo; 2 public class Foo3 { 3 private int result = 20; 4 protected int getResult() { 5 return this.result; 6 } 7 } 1 package test; 2 import demo.Foo3; 3 public class Bar3 extends Foo3 { 4 private int sum = 10; 5 public void reportSum() { 6 sum += getResult(); 7 } 8 } A slightly modified version of the example with the protected keyword is on the slide. If the intention is to restrict access to the result field to package classes and subclasses (package-protected), you must make the access explicit by defining a method written specifically for package and subclass access.
Cancellation of method
Consider the requirement to specify a string that contains some details about the fields of the Employee class. 3 public class Employee { 4 private int empId ; 5 private String name ; 14 // Omitted string 15 16 public String getDetails() { 17 return ID : + empId + Name : + name ; 18 } Although the Employee class has getters that return values for the print statement, it would be nice to have a helper method to get specific information about the employees. Consider the method added to the Employee class to print detailed information about the Employee object. In addition to adding fields or methods to a subclass, you can also modify or change the existing behavior of the parent method (superclass). You may want to specialize this method to describe the Manager object.
Transcribed method call
At runtime, the Java virtual machine calls the method getDetails of the corresponding class. If you comment on the method getDetails of the Manager class from the previous section, what happens when you call m.getDetails()? Answer: Remember that methods are inherited from the parent class. So the method getDetails of the parent class (Employee) is executed runtime. Using the preceding examples, employees and managers can : 5 public static void main(String[] args) { 6 Employee e = new employee(101, Jim Smith, 7 011-12-2345, 100_000.00); 8 Manager m = new Manager(102, Joan Kern, 9 012-23-4567, 110_450.54, Marketing); 10 11 System.out.println(e.getDetails()); 12 System.out.println(m.getDetails()); 13 } The appropriate getDetails method of each class is called: ID: 101 Name: Jim Smith ID: 102 Title: Joan Kern Marketing Department
Call virtual method
What happens when you have the following? 5 public static void main(String[] args) { 6 Employee e = new Manager(102, Joan Kern, 7 012-23-4567, 110_450.54, Marketing); 8 9 System.out.println(e.getDetails()); 10 } At runtime, the runtime type of the object is defined as a handler object: ID: 102 Title: Joan Kern Marketing Department The method that is executed runtime refers to the Manager object. This is an aspect of polymorphism called virtual method calling.
Compilation behaviour in relation to implementation
It is important to remember that there is a difference between the compiler (which checks the availability of each method and field based on a strict definition of the class) and the runtime behavior associated with the defined object. This distinction is an important and powerful aspect of polymorphism: The behavior of an object is determined by its reference at runtime. Since the object you created was a Manager object, when you called the method getDetails runtime, it referred to the method getDetails of the Manager class, even though the variable e is of type Employee. This behavior is called a virtual method call. Message: As a C++ programmer, you only get this behavior if you mark a method with the virtual keyword.
Availability of cancellation methods
An overloaded method should not be less accessible than a method of a parent class. public class Employee { //… Other fields and methods public String getDetails() { … } } 3 public class BadManager extends Employee { 4 private String deptName; 5 // omitted lines 20 @Override 21 private String getDetails() { // compile error 22 return super.getDetails () + 23 Dept : + deptName; 24 } To replace a method, the name and the order of the arguments must be the same. If you change the access to the manager’s method getDetails to private, the BadManager class is not compiled.
Application of polymorphism
Suppose you are asked to create a new class that calculates a bonus for employees based on their salary and role (employee, manager, or engineer): 3 public class BadBonus { 4 public double getBonusPercent(Employee e){ 5 return 0.01; 6 } 7 8 public double getBonusPercent(Manager m){ 9 return 0.03; 10 } 11 12 public double getBonusPercent(Engineer e){ 13 return 0.01; 14 } // Omitted rules
Construction problem
What’s wrong with the example on the slide? Each method performs a calculation based on the passed employee type and returns the premium amount. Think about what happens if you add two or three more types of employees. You will need to add three additional methods and possibly duplicate the code, depending on the business logic needed to calculate the shares. Clearly, this is not the best way to deal with this problem. The code will work, but is not easy to read and will probably generate a lot of duplicate code.
Application ofpolymorphism
It’s a good idea to pass parameters and write methods that use the most generic form your object can take. public class GoodBonus { public static double getBonusPercent(Employee e){ // Code here } // In the Employee class public double calcBonus(){ return this.getSalary() * GoodBonus.getBonusPercent(this); } There is a method to calculate the bonus for each type.
Use the most common form
It is good practice to design and write methods that take the most general form of your object. In this case, Employee is a good base class to start with. But how do you know what kind of object is being broadcast? You will find the answer in the next chapter.
Use of the keyword instanceof
The Java language provides the instance or keyword to determine the class type of an object at runtime. 3 public class GoodBonus { 4 public static double getBonusPercent(Employee e){ 5 if (e instanceof Manager){ 6 return 0.03; 7 }else if (e instanceof Director){ 8 return 0.05; 9 }else { 10 return 0.01; 11 } 12 } 13 } In the GoodBonus class, the method getBonusPercent uses the operator instanceof to determine the type of employee passed to the method.
Override methods of object
The main class of any Java class is java.lang.Object. – All classes will be subclasses of Object by default. – You don’t have to declare that your class Object extends. The compiler does it for you. public class Employee { } Equivalent : public class Employee extends Object { //… } The main class contains several non-final methods, but there are three that are important to override: toString, equals, and hashCode.
Object toString method
The method toString returns a string representation of the object. Employee e = new employee (101, Jim Kern, …) System.out.println (e) ; You can use toString to provide information about the instances: public String toString () { return Employee id : + empId + n+ Employee name : + name ; } This is a better approach to getting detailed information about your class than creating your own getDetails method. The println method is overloaded with different types of parameters. When you call System.out.println(e), the method that accepts the Object parameter is found and called. This method in turn calls the method toString() on the object instance. Message: Sometimes you want to be able to display the name of the class that executes a method. The getClass() method is an Object method that returns an instance of the Class object, and the getName() method returns the fully qualified name of the runtime class. getClass().getName(); // returns the name of this instance of the class. These methods are located in the Object file.
Method of levelling objects
The object comparison method only compares references to objects. – If a class has two objects x and y, then x equals y, and only if x and y refer to the same object. For example: Employee x = new employee (1,Sue,111-11-1111,10.0); Employee y = x; x.equals (y); // true Employee z = new employee (1,Sue,111-11-1111,10.0); x.equals (z); // false! Since we really want to check the content of the Employee object, we need to use the equals : public boolean equals (Object o) { … } The object’s equals method determines (by default) only whether the values of the two object references point to the same object. Basically, the test in the Object class looks like this: For an object (such as an Employee object) that contains values, this comparison is not sufficient, especially if you want to be sure that there is only one employee with a given ID.
Equality gate in the workforce
An example of redefining the method equals in the Employee class compares the equality of each field: 1 @Override 2 public boolean equals (Object o) { 3 boolean result = false; 4 if ((o != null) && (o instanceof Employee)) { 5 employee e = (employee)o; 6 if ((e.empId == this.empId) && 7 (e.name.equals(this.name)) && 8 (e.ssn.equals(this.ssn)) && 9 (e.salary == this.salary)) { 10 result = true; 11 } 12 } return result; 13 } This simple equality test first checks that the passed object is not null, and then that it is an instance of the Employee class (all subclasses are also Employee, so it works). The object is then converted to an employee, and each employee field is checked for equality. Message: The method equals should be used for string types to symbolically check string equality.
@Override annotation
This annotation is used to tell the compiler that a method annotated with @Override is an overloaded method of a superclass or interface. When this annotation is used, the compiler checks to see if you are actually overwriting the method when you think you are. So if you make the common mistake of misspelling a method name or choosing the wrong parameters, you will be warned that your method is not really overloaded like you think. Second, it’s easier to understand your code when you overload methods.
Replace hash code object
The general contract for Object states that if two objects are considered equal (using the method equals), the integer hash code returned for the two objects must also be equal. 1 @Override //generated by NetBeans 2 public hashCode() { 3 int hash = 7; 4 hash = 83 * hash + this.empId; 5 hash = 83 * hash + Objects.hashCode(this.name); 6 hash = 83 * hash + Objects.hashCode(this.ssn); 7 hash = 83 * hash + (int) (Double.doubleToLongBits(this.salary) ^ (Double.doubleToLongBits(this.salary) >> 32))); 8 return hash; 9 } The Java documentation for the Object class says: … Normally you must override the hashCode method when you override this method [equals] to get the general contract of the hashCode method, which states that equal objects must have equal hash codes. The method hashCode is used in conjunction with the method equals in hash-based collections, such as HashMap, HashSet, and Hashtable. Something can easily go wrong with this method, so be careful. The good news is that IDEs like NetBeans can generate hash codes for you. To make your own hash function, the following method gives a reasonable hash value for equal and unequal instances:
- Start with an integer constant that is not zero. Prime numbers lead to fewer collisions with hash codes.
- For each field used in the equality method, compute the int hash code for that field. Note that you can use a hash code for strings.
- Add up the calculated hash codes.
- Bring back the result.
Methods with variable arguments
A variant of method overloading is the case where a method must accept any number of arguments of the same type: public class Statistics { public float Average (int x1, int x2) {} public float Average (int x1, int x2, int x3) {} public float Average (int x1, int x2, int x3, int x4) {} } These three overloaded methods have the same functionality. It would be good to combine these methods into one. Statistics stats = new statistic () ; float avg1 = stats.average(100, 200) ; float avg2 = stats.average(100, 200, 300) ; float avg3 = stats.average(100, 200, 300, 400) ;
Variable number of methods of the same type
A case of overloading is when you need to provide a set of overloaded methods that differ in the number of arguments of the same type. For example, suppose you want to have methods for calculating the average value. You may want to calculate averages for 2, 3 or 4 (or more) integers. Each of these methods performs the same type of calculation – the average of the passed arguments, as in this example: public class Statistics {public float average(int x1, int x2) { return (x1 + x2) / 2 ; }public float average(int x1, int x2, int x3) {return (x1 + x2 + x3) / 3 ;}public float average(int x1, int x2, int x3, int x4) {return (x1 + x2 + x3 + x4) / 4 ;} Java has a handy syntax that lets you combine these three methods into one and specify any number of arguments.
Use of variable arguments
Java has a function called varargs or variable arguments. 1 public class Statistics { 2 public float average(int … nums) { 3 int sum = 0; 4 for (int x : nums) { // iterate the array int nums 5 sum += x; 6 } 7 return ((float) sum / nums.length); 8 } 9 } Note that the argument nums is actually an object of type array int[]. This allows the method to iterate and solve for any number of elements. The average method proposed above accepts any number of integer arguments. The notation (int… nums) converts the list of arguments passed to the method Average into an array object of type int. Message: Methods that use varargs can’t take parameters either – calling average() is legitimate. You will see varargs as optional parameters used in the NIO.2 API in the lesson on file I/O in Java. To take this into account, you can rewrite the average method presented above as follows: public float average(int… nums) {int sum = 0; float result = 0;if (nums.length > 0) {for (int x : nums) // iteration of the array int numssum += x;result = (float) sum / nums.length;}return (result);}
Offset of object references
After using the instanceof operator to verify that the object you receive as an argument is a subclass, you can access the object’s full functionality by casting a reference : 4 public static void main(String[] args) { 5 Employee e = new Manager(102, Joan Kern, 6 012-23-4567, 110_450.54, Marketing); 7 8 if (e instanceof Manager){ 9 Manager m = (Manager) e; 10 m.setDeptName(HR); 11 System.out.println(m.getDetails()); 12 } 13 } Without a conversion to Manager, the setDeptName method will not compile. Although a generic reference to a superclass is useful for passing objects, it may be necessary to use a method of a subclass. In the above example, you need the setDeptName method of the Manager class. To satisfy the compiler, you can refer to a specific class from a generic superclass. However, there are rules for link casting. You will see them in the next chapter.
Rules on upward dumping
Upward throws are always allowed and require no intervention from the thrower. Director d = new director() ; Manager m = new manager() ;
Rules relating to deposit
To do a cast down, the compiler must be sure that the cast is feasible. 5 Employee e = new Manager(102, Joan Kern, 6 012-23-4567, 110_450.54, Marketing); 7 8 Manager m = (Manager)e; // ok 9 Engineer eng = (Manager)e; // compile error 10 System.out.println(m.getDetails())
Downward Chestnut
For down-conversion, the compiler simply determines whether such a conversion is possible; if it is to a subclass, it is quite possible that the conversion will succeed. Note that the conversion at runtime causes a java.lang.ClassCastException if the reference to the object is to a superclass and not to a class type or subclass. Finally, any conversion outside the class hierarchy fails, for example. B. conversion from a manager instance to an engineer instance. A manager and an engineer are employees, but a manager is not an engineer.
Static keyword
The static modifier is used to declare fields and methods as class-level resources. Static members of the class :
- Can be used without object instances
- Used when the problem is best solved without objects.
- Used when objects of the same type must have common fields.
- Should not be used to bypass Java’s object-oriented functions, unless there is a good reason to do so.
Static methods
Static methods are methods that can be called even if the class in which they are declared has not yet been instantiated. Static methods :
- They are called class methods
- Useful for APIs that are not object-oriented – java.lang.Math contains many static methods.
- Usually used instead of constructors to perform tasks related to the initialization of objects.
- It is not possible to access non-static members within the same class.
Use of static variables and methods Example
The java.lang.Math class contains methods and constants for performing basic numerical operations, such as. B. Elementary exponent, logarithm, square root and trigonometry. All methods and constants of the Math class are static, so you call them directly from within the class, as in the following examples. 3 public class A01MathTest { 4 public static void main(String[] args) { 5 System.out.println(Random : + Math.random() * 10); 6 System.out.println(Square root : + Math.sqrt(9.0)); 7 System.out.println(Random rounding : + 8 Math.round(Math.random()*100)); 9 System.out.println(Abs : + Math.abs(-9)); 10 } 11 } Here are some examples of conclusions that can be drawn from this example: By mistake: 7.064916553599695 Square root : 3.0 Accident on roundabout: 35 Para : 9
Performance of static methods
Use the keyword static for the method. The method has parameters and return types as usual. 3 import java.time.LocalDate; 4 5 public class StaticHelper { 6 7 public static void printMessage(String message) { 8 System.out.println(Message for + 9 LocalDate.now() + : + message); 10 } 11 12 } Static methods or class methods can be without an object instance.
Limitations of the static method
Static methods can be used before all instances of the class they encompass have been created. If a class contains both static and instance components, you cannot access the instance components in a static context.
Static method call
When calling static methods, you must : – Specify the location of the method with the class name if the method is in a different class than the calling class – Not required for methods in the same class. – Avoid using an object reference to call a static method. double d = Math.random() ; StaticHelper.printMessage(Hello) ;
Static variables
Static variables are variables that are accessible even if the class in which they are declared has not yet been created. The static variables are :
- Class variables called
- Limited to one copy per JVM
- Used to contain general data:
- Static methods store data in static variables.
- All object instances have a copy of all static variables.
- It is initialized when the containing class is loaded for the first time.
Loading class
Classes provided by application developers are usually downloaded on demand (first use). Static variables are initialized when their surrounding class is loaded. An attempt to access a static class member may result in the class being loaded.
Definition of static variables
A static variable is defined if the keyword static precedes the definition of the variable type. Static variables are useful for storing shared data. All object instances share a single copy of all static variables. 4 public class StaticCounter { 5 private static int counter = 0; 6 7 public static getCount() { 8 return counter; 9 } 10 11 public static void increment(){ 12 counter++; 13 } 14 }
Use of static variables
When accessing static variables, you must use : – Specify the location of the variable with the name of the class if the variable is in a different class than the calling class – Not required for variables in the same class. – Avoid using an object reference to access a static variable. 5 public static void main(String[] args) { 6 System.out.println(Start : + StaticCounter.getCount()); 7 StaticCounter.increment(); 8 StaticCounter.increment(); 9 System.out.println(End : + StaticCounter.getCount()); 10}
Object references to static members
Just as you should avoid using object references for static methods, you should avoid using object references to access static variables. The use of a private access level prevents direct access to static variables. Example Conclusion:
Static initializations
A static initialization block is a block of code preceded by the static keyword. 3 public class A04StaticInitializerTest { 4 private static final boolean[] switches = new boolean[5]; 5 6 static{ 7 System.out.println(Initialization….); 8 for (int i=0; i<5; i++){ 9 switches[i] = true; 10 } 11 } 12 13 public static void main(String[] args) { 14 switches[1] = false; switches[2] = false; 15 System.Out.print(Switch settings: ); 16 for (boolean curSwitch:switches){ 17 if (curSwitch){System.Out.print(1);} 18 else {System.Out.print(0);} 19 } Here are some important facts about static initialization blocks:
- They are executed only once when the class is loaded.
- They are used to initialize static variables.
- A class may contain one or more static initialization blocks.
- They can appear anywhere in the body of the class.
- Blocks are called in the order they appear in the source code.
Consider using static initializers when non-trivial code is needed to initialize static variables.
Static input
The static import operator makes members of static classes available by name only. Each of the following rules is given: import static java.lang.Math.random; import static java.lang.Math.* ; The call to the Math.random() method can be written as follows: public class StaticImport { public static void main(String[] args) { double d = random(); } } Excessive use of static imports can have a negative effect on the readability of your code. Avoid adding multiple static imports to a class.
Design model
Design templates include:
- Reusable solutions to common software development problems
- Documented in the pattern directories – Design Patterns : Elements of reusable object-oriented software, written by Erich Gamma and others (Gang of Four)
- Vocabulary for design discussion
Guidelines for design patterns
There are template directories for many programming languages. Most traditional design patterns are applicable to any object-oriented programming language. One of the most popular books, Design Patterns: Elements of Reusable Object-Oriented Software, uses a combination of C++, Smalltalk and diagrams to show possible implementations of the models. Many Java developers still refer to this book because the concepts are applicable to any object-oriented language.
Singleton model
The singleton design pattern describes an implementation of a class that can only be instantiated once.
Implementing the singleton model
A singleton is a class of which only one instance can be created, and which provides a global access point to this instance. Singletons are useful for providing a unique data source or functionality to other Java objects. For example, you can use a singleton to access your data model from the application or to define a recorder that the rest of the application can use.
To implement the singleton design pattern:
- Use a static reference to refer to a single instance. Making a reference an endpoint ensures that it never points to another instance.
- Add a single private constructor to the singleton class. The private modifier only allows access to the same class, which prohibits any attempt to instantiate the singleton class other than in step 1.
- The public method of the factory returns a copy of the reference to the singleton. This method is declared as static to access the static field declared in step 1. Step 1 can use a public variable, so no factory method is needed. Factory methods offer more flexibility (e.g., implementing a single-thread solution for each thread) and are used in most single-thread implementations.
- With the singleton model, the creation of an instance is limited to the first request (lazy initialization). To get a reference to the singleton, call the getInstance method: SingletonClass ref = SingletonClass.getInstance() ;
Singleton: Example
Singletons are perfect for storing data that applies to the entire application. In this example, some database configuration data is stored in Singleton. 3 public final class DbConfigSingleton { 4 private final String hostName; 5 private final String dbName; 6 // Omitted strings 10 private static final DbConfigSingleton instance = 11 new DbConfigSingleton(); 12 13 private DbConfigSingleton(){ 14 // Values are loaded from file in practice 15 hostName = dbhost.example.com; 16 // strings omitted 20 } 21 22 public static DbConfigSingleton getInstance() { 23 return instance; 24 }
Unchangeable classes
Unchanging class:
- It is a class whose object state cannot be changed after its creation.
- Any change to an object gives rise to a new unchangeable object.
- Example: Java.lang.String objects, any change to an existing String object creates another String; for example, replacing a character or creating a substring creates new objects.
Rules for creating an immutable class in Java:
- The state of an immutable object cannot be changed after its creation. Each change results in the creation of a new immutable object.
- Declare the class as finite, so that it cannot be extended.
- All fields are declared private, so direct access to them is prohibited.
- There is no setter method for variables.
- All fields of an invariant class must be finite.
- All fields are initialized by the constructor.
- The object must be finite, so that the subclass cannot change the immutability of the main class.
Creating an immutable class in Java
The following example illustrates an immutable class in Java, where all fields of the class remain immutable and the class itself is finite to avoid the risk of immutability due to inheritance. public final class Contacts { private final String firstName ; private final String lastName ; public Contacts(String fname,String lname) { this.firstName= fname ; this.lastName = lname ; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public String toString() { return firstName + – + lastName + – + lastName; } }
The benefits of immutable classes in Java
- Immutable objects are secure by default and can be shared in a parallel environment without synchronization.
- An immutable object improves the performance of a Java application by reducing synchronization in the code.
- Reusability: You can cache and reuse immutable objects, such as strings and integer literals.
Message: If an immutable class has many optional and required fields, you can also use the Builder design pattern to create an immutable class in Java.We all know classes are cool because they allow us to do the same thing over and over again without having to repeat ourselves. So what happens when you want to override those methods and still allow polymorphism?