Unit 3 Java-1
Unit 3 Java-1
1.Interface in Java
Interface
An interface in Java is a blueprint of a class. It has static constants and abstract methods.
The interface in Java is a mechanism to achieve abstraction. There can be only abstract
methods in the Java interface, not method body. It is used to achieve abstraction and multiple
inheritance in Java.
In other words, you can say that interfaces can have abstract methods and variables. It cannot
have a method body.
Java Interface also represents the IS-A relationship.
It cannot be instantiated just like the abstract class.
Since Java 8, we can have default and static methods in an interface.
Since Java 9, we can have private methods in an interface.
Why use Java interface?
There are mainly three reasons to use interface. They are given below.
It is used to achieve abstraction.
By interface, we can support the functionality of multiple inheritance.
It can be used to achieve loose coupling.
interface in java
The relationship between classes and interfaces
As shown in the figure given below, a class extends another class, an interface extends
another interface, but a class implements an interface.
In this example, the Printable interface has only one method, and its implementation is
provided in the A6 class.
interface printable{
void print();
}
class A6 implements printable{
public void print(){System.out.println("Hello");}
File: TestInterface1.java
Output:
drawing circle
Since Java 8, we can have static method in interface. Let's see an example:
File: TestInterfaceStatic.java
interface Drawable{
void draw();
static int cube(int x){return x*x*x;}
}
class Rectangle implements Drawable{
public void draw(){System.out.println("drawing rectangle");}
}
class TestInterfaceStatic{
public static void main(String args[]){
Drawable d=new Rectangle();
d.draw();
System.out.println(Drawable.cube(3));
}}
Output:
drawing rectangle
27
Abstract class and interface both are used to achieve abstraction where we can declare the
abstract methods. Abstract class and interface both can't be instantiated.
Example of abstract class and interface in Java
Let's see a simple example where we are using interface and abstract class both.
//Creating abstract class that provides the implementation of one method of A interface
abstract class B implements A{
public void c(){System.out.println("I am C");}
}
//Creating subclass of abstract class, now we need to provide the implementation of rest of
the methods
class M extends B{
public void a(){System.out.println("I am a");}
public void b(){System.out.println("I am b");}
public void d(){System.out.println("I am d");}
}
Output:
I am a
I am b
I am c
I am d
A functional interface is an interface that contains only one abstract method. They can have
only one functionality to display. From Java 8 onwards, lambda expressions can be used to
represent the instance of a functional interface. A functional interface can have any number of
default methods. Runnable, ActionListener, and Comparable are some of the examples of
functional interfaces.
Functional interfaces are included in Java SE 8 with Lambda expressions and Method
references in order to make code more readable, clean, and straightforward. Functional
interfaces are interfaces that ensure that they include precisely only one abstract method.
Functional interfaces are used and executed by representing the interface with an annotation
called @FunctionalInterface. As described earlier, functional interfaces can contain only one
abstract method. However, they can include any quantity of default and static methods.
In Functional interfaces, there is no need to use the abstract keyword as it is optional to use
the abstract keyword because, by default, the method defined inside the interface is abstract
only. We can also call Lambda expressions as the instance of functional interface.
Example 2
@FunctionalInterface
interface sayable{
void say(String msg);
}
public class FIE implements sayable{
public void say(String msg){
System.out.println(msg);
}
public static void main(String[] args) {
FIE fie = new FIE();
fie.say("Hello there");
}
}
Output: Hello there
Example 3
@FunctionalInterface
interface sayable{
void say(String msg); // abstract method
// It can contain any number of Object class methods.
int hashCode();
String toString();
boolean equals(Object obj);
}
public class FunctionalInterfaceExample2 implements sayable{
public void say(String msg){
System.out.println(msg);
}
public static void main(String[] args) {
FunctionalInterfaceExample2 fie = new FunctionalInterfaceExample2();
fie.say("Hello there");
}
}
Java SE 8 included four main kinds of functional interfaces which can be applied in multiple
situations as mentioned below:
Consumer
Predicate
Function
Supplier
class Test
{
public static void main(String args[])
{
// lambda expression to implement above
// functional interface. This interface
// by default implements abstractFun()
FuncInterface fobj = (int x)->System.out.println(2*x);
Output
10
Zero Parameter
Single Parameter
Multiple Parameters
It is not mandatory to use parentheses if the type of that variable can be inferred from the
context
Output : 1,,2 ,3 , 4 2 ,4
// lambda expression multiplication for two parameters This expression also implements
// 'FuncInter1' interface
FuncInter1 multiply = (int x, int y) -> x * y;
Output
Addition is 9
Multiplication is 18
Hello Geek
Note: Lambda expressions are just like functions and they accept parameters just like
functions.
3.Java Method References
Java provides a new feature called method reference in Java 8. Method reference is used to
refer method of functional interface. It is compact and easy form of lambda expression. Each
time when you are using lambda expression to just referring a method, you can replace your
lambda expression with method reference. In this tutorial, we are explaining method
reference concept in detail.
Syntax
ContainingClass::staticMethodName
Example 1
In the following example, we have defined a functional interface and referring a static
method to it's functional method say().
interface Sayable{
void say();
}
public class MethodReference {
public static void saySomething(){
System.out.println("Hello, this is static method.");
}
public static void main(String[] args) {
// Referring static method
Sayable sayable = MethodReference::saySomething;
// Calling interface method
sayable.say();
}
}
Output:
Hello, this is static method.
Example 2
You can also override static methods by referring methods. In the following example, we
have defined and overloaded three add methods.
import java.util.function.BiFunction;
class Arithmetic{
public static int add(int a, int b){
return a+b;
}
public static float add(int a, float b){
return a+b;
}
public static float add(float a, float b){
return a+b;
}
}
public class MethodReference4 {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer>adder1 = Arithmetic::add;
BiFunction<Integer, Float, Float>adder2 = Arithmetic::add;
BiFunction<Float, Float, Float>adder3 = Arithmetic::add;
int result1 = adder1.apply(10, 20);
float result2 = adder2.apply(10, 20.0f);
float result3 = adder3.apply(10.0f, 20.0f);
System.out.println(result1);
System.out.println(result2);
System.out.println(result3);
}
}
Output:
30
30.0
30.0
Like static methods, you can refer instance methods also. In the following example, we are
describing the process of referring the instance method.
Syntax
containingObject::instanceMethodName
Example 1
In the following example, we are referring non-static methods. You can refer methods by
class object and anonymous object.
interface Sayable{
void say();
}
public class InstanceMethodReference {
public void saySomething(){
System.out.println("Hello, this is non-static method.");
}
public static void main(String[] args) {
InstanceMethodReference methodReference = new InstanceMethodReference(); //
Creating object
// Referring non-static method using reference
Sayable sayable = methodReference::saySomething;
// Calling interface method
sayable.say();
// Referring non-static method using anonymous object
Sayable sayable2 = new InstanceMethodReference()::saySomething; // You can use
anonymous object also
// Calling interface method
sayable2.say();
}
}
Output:
Hello, this is non-static method.
Hello, this is non-static method.
3) Reference to a Constructor
You can refer a constructor by using the new keyword. Here, we are referring constructor
with the help of functional interface.
Syntax
ClassName::new
Example
interface Messageable{
Message getMessage(String msg);
}
class Message{
Message(String msg){
System.out.print(msg);
}
}
public class ConstructorReference {
public static void main(String[] args) {
Messageable hello = Message::new;
hello.getMessage("Hello");
}
}
Output:
Hello
4.Stream In Java
Introduced in Java 8, Stream API is used to process collections of objects. A stream in Java is
a sequence of objects that supports various methods which can be pipelined to produce the
desired result.
Syntax
Stream<T> stream;
Here T is either a class, object, or data type depending upon the declaration.
A stream is not a data structure instead it takes input from the Collections, Arrays or I/O
channels.
Streams don’t change the original data structure, they only provide the result as per the
pipelined methods.
Each intermediate operation is lazily executed and returns a stream as a result, hence various
intermediate operations can be pipelined. Terminal operations mark the end of the stream and
return the result.
Different Operations On Streams
There are two types of Operations in Streams:
Intermediate Operations
Terminate Operations
Intermediate Operations
Intermediate Operations are the types of operations in which multiple methods are
chained in a row.
No Storage
Pipeline of Functions
Laziness
Can be infinite
Can be parallelized
Can be created from collections, arrays, Files Lines, Methods in Stream, IntStream etc.
1. collect()
The collect method is used to return the result of the intermediate operations performed on
the stream.
Note: Intermediate Operations are running based on the concept of Lazy Evaluation, which
ensures that every method returns a fixed value(Terminal operation) before moving to the
next method.
System.out.println(result);
System.out.println(show);
System.out.println(squareSet);
System.out.println(even);
}
}
Output
Base 64 is an encoding scheme that converts binary data into text format so that encoded
textual data can be easily transported over network un-corrupted and without any data
loss.(Base 64 format reference).
The Basic encoding means no line feeds are added to the output and the output is mapped to a
set of characters in A-Za-z0-9+/ character set and the decoder rejects any character outside of
this set.
Explanation: In above code we called Base64.Encoder using getEncoder() and then get the
encoded string by passing the byte value of actualString in encodeToString() method as
parameter.
import java.util.*;
public class GFG {
public static void main(String[] args)
{
import java.util.*;
public class GFG {
public static void main(String[] args)
{
// create an encoded String to decode
String encode = "SW5kaWEgVGVhbSB3aWxsIHdpbiB0aGUgQ3Vw";
// print encoded String
System.out.println("Encoded String:\n+ encoded);
// decode into String from encoded format
byte[] actualByte = Base64.getDecoder() .decode(encoded);
Java provides a new method forEach() to iterate the elements. It is defined in Iterable and
Stream interface. It is a default method defined in the Iterable interface. Collection classes
which extends Iterable interface can use forEach loop to iterate elements.
This method takes a single parameter which is a functional interface. So, you can pass
lambda expression as an argument.
import java.util.ArrayList;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> gamesList = new ArrayList<String>();
gamesList.add("Football");
gamesList.add("Cricket");
gamesList.add("Chess");
gamesList.add("Hocky");
System.out.println("------------Iterating by passing lambda expression--------------");
gamesList.forEach(games -> System.out.println(games));
} }
Output:
------------Iterating by passing lambda expression--------------
Football Cricket Chess Hocky
import java.util.ArrayList;
import java.util.List;
public class ForEachExample {
public static void main(String[] args) {
List<String> gamesList = new ArrayList<String>();
gamesList.add("Football");
gamesList.add("Cricket");
gamesList.add("Chess");
gamesList.add("Hocky");
System.out.println("------------Iterating by passing method reference---------------");
gamesList.forEach(System.out::println);
} }
Output:
------------Iterating by passing method reference---------------
Football Cricket Chess Hocky
You can pass any object as a resource that implements java.lang.AutoCloseable, which
includes all objects which implement java.io.Closeable.
By this, now we don’t need to add an extra finally block for just passing the closing
statements of the resources. The resources will be closed as soon as the try-catch block is
executed.
Syntax: Try-with-resources
Exceptions:
Now, let us discuss both the possible scenarios which are demonstrated below as an example
as follows:
Case 1: Single resource
Case 2: Multiple resources
// Class
class GFG {
// Adding resource
FileOutputStream fos = new FileOutputStream("gfgtextfile.txt")) {
Resource are closed and message has been written into the gfgtextfile.txt
@NonNull List<String>
List<@NonNull String> str
Arrays<@NonNegative Integer> sort
@Encrypted File file
@Open Connection connection
void divideInteger(int a, int b) throws @ZeroDivisor ArithmeticException
Note - Java created type annotations to support improved analysis of Java programs. It
supports way of ensuring stronger type checking.
For compatibility reasons, repeating annotations are stored in a container annotation that is
automatically generated by the Java compiler. In order for the compiler to do this, two
declarations are required in your code.
1. Declare a repeatable annotation type
2. Declare the containing annotation type
@Repeatable(Games.class)
@interfaceGame{
String name();
String day();
}
The value of the @Repeatable meta-annotation, in parentheses, is the type of the container
annotation that the Java compiler generates to store repeating annotations. In the following
example, the containing annotation type is Games. So, repeating @Game annotations is
stored in an @Games annotation.
@interfaceGames{
Game[] value();
}
Note - Compiler will throw a compile-time error, if you apply the same annotation to a
declaration without first declaring it as repeatable.
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
// Declaring repeatable annotation type
@Repeatable(Games.class)
@interfaceGame{
String name();
String day();
}
// Declaring container for repeatable annotation type
@Retention(RetentionPolicy.RUNTIME)
@interfaceGames{
Game[] value();
}
// Repeating annotation
@Game(name = "Cricket", day = "Sunday")
@Game(name = "Hockey", day = "Friday")
@Game(name = "Football", day = "Saturday")
public class RepeatingAnnotationsExample {
public static void main(String[] args) {
// Getting annotation by type into an array
Game[] game =
RepeatingAnnotationsExample.class.getAnnotationsByType(Game.class);
for (Gamegame2 : game) { // Iterating values
System.out.println(game2.name()+" on "+game2.day());
}
}
}
OUTPUT:
Cricket on Sunday
Hockey on Friday
Football on Saturday
Java Module System is a major change in Java 9 version. Java added this feature to collect
Java packages and code into a single unit called module.
In earlier versions of Java, there was no concept of module to create modular Java
applications, that why size of application increased and difficult to move around. Even JDK
itself was too heavy in size, in Java 8, rt.jar file size is around 64MB.
To deal with situation, Java 9 restructured JDK into set of modules so that we can use only
required module for our project.
Apart from JDK, Java also allows us to create our own modules so that we can develop
module based application.
The module system includes various tools and options that are given below.
o Includes various options to the Java tools javac, jlink and java where we can specify
module paths that locates to the location of module.
o Modular JAR file is introduced. This JAR contains module-info.class file in its root
folder.
o JMOD format is introduced, which is a packaging format similar to JAR except it can
include native code and configuration files.
o The JDK and JRE both are reconstructed to accommodate modules. It improves
performance, security and maintainability.
o Java defines a new URI scheme for naming modules, classes and resources.
o Module name
o What does it export
o What does it require
Module Name
It is a name of module and should follow the reverse-domain-pattern. Like we name packages,
e.g. com.javatpoint.
Note: The name of the directory containing a module's sources should be equal to the name of
the module, e.g. com.javatpoint.
Create a file module-info.java, inside this file, declare a module by using module identifier
and provide module name same as the directory name that contains it. In our case, our directory
name is com.javatpoint.
ADVERTISEMENT
1. module com.javatpoint{
2.
3. }
Leave module body empty, if it does not has any module dependency. Save this file
inside src/com.javatpoint with module-info.java name.
Now, create a Java file to compile and execute module. In our example, we have
a Hello.java file that contains the following code.
1. class Hello{
2. public static void main(String[] args){
3. System.out.println("Hello from the Java module");
4. }
5. }
After compiling, it will create a new directory that contains the following structure.
Output:
1. javap mods/com.javatpoint/module-info.class
See, we created an empty module but it contains a java.base module. Why? Because all Java
modules are linked to java.base module and it is default module.
With the help of Diamond operator, we can create an object without mentioning the generic
type on the right hand side of the expression. But the problem is it will only work with
normal classes. Suppose you want to use the diamond operator for anonymous inner class
then compiler will throw error message like below:
Output:
prog.java:9: error: cannot infer type arguments for Geeksforgeeks
Geeksforgeeks obj = new Geeksforgeeks () {
^
reason: cannot use '' with anonymous inner classes
where T is a type-variable:
T extends Object declared in class Geeksforgeeks
1 error
Java developer extended the feature of the diamond operator in JDK 9 by allowing the
diamond operator to be used with anonymous inner classes too. If we run the above code
with JDK 9, then code will run fine and we will generate the below output.
Output:
Addition of two numbers: 30
Java 12 improved the traditional switch statement and made it more useful. Java 13 further
introduced new features. Before going into the details of new features, let’s have a look at
the drawbacks faced by the traditional Switch statement.
The above code works by matching the corresponding case and executing the particular code
block. As long as you provide the necessary break statements, it works fine.
}
Here, if we pass 001, the first case matches, and the code block executes. But due to missing
break, execution falls through and continues for case 002. We get the following wrong
output:
It's a laptop!
It's a desktop!
Clearly, this is not the intended output. It is a result of accidentally missing out break
statements.
2. Multiple values per case not supported:
There may be situations where similar processing is required for multiple case values. But
the traditional switch makes to follow the fall through behaviors.
case 001:
case 002:
case 003:
System.out.println("It's an electronic gadget!");
Much improved switch accepts multiple values per case.
Enhancements to switch statements were introduced by Java 12 and then further modified by
Java 13. Let’s dive into the important features of this improved version of the Switch
statement.
1. Supports multiple values per case:
With multiple values being specified per case, it simplifies the code structure and eliminates
the need for using fall through.
The values need to be separated by commas and break should follow the case block.
switch (itemCode) {
case 001, 002, 003 :
System.out.println("It's an electronic gadget!");
break;
13.Yield Keyword
In Java, the yield keyword was introduced as part of the enhancement of the switch
expression in Java 12 (as a preview feature) and then fully integrated in Java 14. It allows for
a value to be returned from a switch expression, making the switch statement more flexible
and expressive.
Understanding yield in Switch Expressions
Prior to Java 12, switch statements were primarily used for control flow, but they couldn't
directly return values. With the introduction of switch expressions, you can now use switch
statements to produce values.
In the example above, the switch expression assigns a value to the variable dayType. It uses
the arrow (->) syntax for cases and does not require the yield keyword because it returns a
simple value.
However, when you have more complex logic within a case, you can use yield to return a
value from that case:
Explanation
1. Switch Expression: The switch statement in this example is used as an expression
that returns a value.
2. Arrow Syntax: The -> arrow syntax simplifies returning a single value.
3. Yield Statement: When more complex logic is required inside a case, the yield
statement is used to return a value from the block. This is particularly useful when the
case block contains multiple statements.
Clarity and Conciseness: The code is clearer and more concise compared to
traditional switch statements.
Expressions Over Statements: Allows switch to be used as an expression, making it
more powerful and flexible.
Less Error-Prone: Reduces common errors associated with traditional switch
statements, such as fall-through.
In this article, we are going to discuss the Java 15 text blocks feature to declare multi-line
strings most efficiently. We all know that how we can
declare multi-line strings and that too quite easily with the help of concatenation, string’s join
method, StringBuilder append method, etc. Now the question will arise in our mind that if
everything is this simple then what is the need for a new feature i.e. text blocks to declare
multi-line strings. So let’s first try to understand the problem and then we will understand the
need for a text block.
In earlier releases of the JDK, embedding multi-line code snippets required a tangled mess of
explicit line terminators, string concatenations, and delimiters. Text blocks eliminate most of
these obstructions, allowing you to embed code snippets and text sequences more or less as-
is.
A text block is an alternative form of Java string representation that can be used anywhere a
traditional double-quoted string literal can be used. Text blocks begin with a “”” (3 double-
quote marks) observed through non-obligatory whitespaces and a newline. For example:
Java
Note: Since Java 15, text blocks are available as a standard feature. With Java 13 and 14, we
needed to enable it as a preview feature.
The object created from text blocks is java.lang.String with the same properties as a regular
string enclosed in double quotes. This includes the presentation of objects and the interning.
Continue with text1 and text2 from the previous examples,
Java
// Both text1 and text2 are strings of equal value
text1.equals(text2) // true
Text blocks may be utilized in the region of a string literal to enhance the clarity and
readability of the code. This typically takes place whilst a string literal is used to symbolize a
multi-line string. In this example there’s a substantial muddle from citation marks, newline
escapes, and concatenation operators:
Java
// ORIGINAL
String message = "A-143, 9th Floor, Sovereign Corporate Tower,\n" +
"Sector-136, Noida,\n" +
"Uttar Pradesh - 201305";
15.Sealed Classes
Sealed classes and interfaces were introduced in Java 15 as a preview feature and became a
standard feature in Java 17. Sealed classes and interfaces allow you to restrict which other
classes or interfaces can extend or implement them. This helps to model your domain more
precisely and enhances maintainability and security by controlling the inheritance hierarchy.
Syntax
Sealed classes and interfaces are defined using the sealed keyword followed by the permits
clause, which lists the permitted subclasses.
Explanation
1. Sealed Class: The Shape class is declared as sealed and specifies the subclasses
Circle, Rectangle, and Square using the permits clause.
2. Final Subclasses: The permitted subclasses are declared as final, meaning they
cannot be extended further. Alternatively, subclasses can be non-sealed, allowing
further subclassing, or sealed themselves.
1. Control Over Inheritance: Ensures that the class hierarchy remains controlled and
predictable.
2. Exhaustiveness Checking: Helps the compiler perform exhaustive checking, which
is useful for pattern matching and switch expressions.
3. Enhanced Design: Provides a clear and well-defined class hierarchy, improving the
design and maintenance of APIs and libraries.
Defining Fixed Sets of Related Classes: When you have a fixed set of related classes
and want to ensure no other classes can extend your base class.
Enum-Like Structures: When you need more complex behaviour than enums but
still want to restrict the set of possible types.
Exhaustive Pattern Matching: In combination with pattern matching and switch
expressions, sealed classes allow for exhaustive checks at compile time.
Conclusion
Sealed classes in Java provide a powerful way to control the inheritance hierarchy of your
classes and interfaces. They allow you to define a restricted set of subclasses, ensuring better
design, enhanced security, and more predictable code behaviour. By leveraging sealed
classes, you can create more robust and maintainable applications.