Unit 3.2
Unit 3.2
Unit 3.2
The Stream API, introduced in Java 8. It allows for functional-style operations on collections of objects. This
API helps in writing more readable, efficient, and concise code. Streams support various operations such as
filtering, mapping, and reducing.
Characteristics of Streams
Sequence of Elements: Streams represent a sequence of elements of a specific type. This sequence can be
ordered or unordered depending on the source.
Pipelining: Streams support pipelining of multiple operations, where the result of one operation is passed
directly to the next. This allows for a fluent programming style.
Laziness: Streams are lazy, meaning that intermediate operations (like filter, map, etc.) are not executed
until a terminal operation (like collect, forEach, etc.) is invoked. This allows for efficient computation
by optimizing the intermediate operations.
Consumable: Streams can be traversed only once. After a terminal operation is performed, the stream is
considered consumed and cannot be reused. To traverse the data again, a new stream needs to be created.
Creating Streams
Streams can be created from various data sources such as collections, arrays, or specific methods:
// From a Collection
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
// From an Array
Stream<String> stream = Stream.of("a", "b", "c");
// From Values
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Example: create a list of names and filter out the names that are shorter than 5 characters, convert the
remaining names to uppercase, and collect the results into a new list.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
Basic Syntax
Annotations are defined with the @ symbol, followed by the annotation name:
@Override
public String toString() {
return "Example";
}
Built-in Annotations
@Override
Indicates that a method is overriding a method in a superclass.
Helps catch errors if the method signatures don't match.
@Deprecated
o Marks a method or class as deprecated, indicating it should no longer be used.
o Generates a warning if the deprecated code is used.
@SuppressWarnings
o Tells the compiler to suppress specific warnings.
Custom Annotations
You can create your own annotations using the @interface keyword.
Example:
@interface MyAnnotation {
String value();
int number() default 0;
}
Usage:
Meta-annotations are annotations that apply to other annotations. Java provides several built-in meta-
annotations:
1. @Retention
o Specifies how long the annotation should be retained.
o Values: RetentionPolicy.SOURCE, RetentionPolicy.CLASS, RetentionPolicy.RUNTIME.
2. @Target
o Specifies where the annotation can be applied (e.g., method, field, type).
o Values: ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, etc.
3. @Documented
o Indicates that the annotation should be documented by javadoc and similar tools.
4. @Inherited
o Indicates that the annotation type can be inherited from the superclass.
Example:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MyRuntimeAnnotation {
String value();
}
Try-with-resources in Java
Try-with-resources is a feature introduced in Java 7 that simplifies handling resources. A resource is any
object that needs to be properly closed after use, such as files, network connections, or databases.
You declare resource variables within parentheses after the try keyword. These variables are automatically
closed at the end of the try block, even in case of exceptions.
Example:
Key points:
Benefits:
A switch expression is a new way to use the switch statement in Java. It was Introduced in Java 12 and made
standard in Java 14. It allows you to return a value from the switch statement. This makes your code cleaner
and easier to read.
Syntax
Characteristics
Conciseness: Switch expressions use the -> syntax, which makes the code shorter and cleaner.
Exhaustiveness: You must handle all possible cases, either explicitly or with a default case.
Return Values: Switch expressions return a value that you can directly assign to a variable.
Multiple Labels: You can group multiple case labels together.
Yield Statement: You can use the yield statement to return values in more complex scenarios.
Example
Explanation:
The yield keyword is used to return a value from a block of code within a switch expression. It allows for
more complex logic before returning a value.
Let's create a program that returns the number of days in a month based on the month number:
Explanation:
Example:
System.out.println(message);
System.out.println(number);
}
}
Key Points:
var can only be used for local variables (inside methods, initializers, etc.).
The type of the variable is created at compile-time.
Text Blocks
Text Blocks allow you to write multi-line strings more easily. It was introduced in Java 13.
Text block uses triple double quotes (""") to create the multiline strings.
Example:
System.out.println(data);
}
}
Key Points:
A record automatically generates several useful methods such as equals(), hashCode(), toString(),
and accessor methods for the fields.
Key Points:
Record Declaration: The record is declared using the record keyword followed by the record name
and the list of fields (also known as the record components).
Auto-generated Methods: The compiler automatically generates a constructor, accessor methods (e.g.,
name(), age()), equals(), hashCode(), and toString() methods.
Immutability: Records are designed to be immutable. The fields are final by default, and there are no
setters.
Custom Methods: You can add custom methods within the record declaration to provide additional
functionality.
Example
In this example:
By using sealed classes, you can have more control over your class hierarchy, ensuring that only specified
classes are allowed to be subclasses.
Syntax
The basic syntax for a sealed class involves the sealed keyword followed by a permits clause, which lists
the classes that are allowed to extend the sealed class.
Example
Let's consider a simple example involving a sealed class hierarchy for shapes:
Key Points:
Sealed Declaration: The sealed class Shape uses the sealed keyword and the permits clause to
specify which classes can extend it (Circle, Rectangle, and Triangle in this case).
Subclasses:
o Circle and Rectangle are final, meaning they cannot be further extended.
o Triangle is non-sealed, which means it can be extended by other classes.
Example Usage:
Detailed Explanation:
The Shape class is declared as sealed, meaning its subclasses are restricted to those listed in the
permits clause.
2. Final Subclass:
The Circle class is declared as final, indicating that no further subclasses can extend Circle.
3. Non-Sealed Subclass:
The Triangle class is declared as non-sealed, meaning it can be extended by other classes.
Benefits:
Control Over Inheritance: Sealed classes provide better control over which classes can extend a
particular class, leading to a more secure and predictable class hierarchy.
Enhanced Maintainability: By restricting the inheritance, it becomes easier to manage and understand
the relationships between classes.
Improved Exhaustiveness Checking: Sealed classes can improve the exhaustiveness checking in
switch expressions and patterns, ensuring that all possible subclasses are accounted for.