Untitled
unknown
plain_text
2 years ago
28 kB
11
Indexable
javacore.organization
functionalprogramming.function.methodreference
1. FunctionalInterfaces
/**
* Functional interfaces allow developers to use functional programming over the concepts from OOP. It is functional
* interfaces that make it possible to use lambda expressions, method references, and other functional stuff.
* <p></p>
* <p></p>
* <p>
* Functions and Interfaces
* <p></p>
* If an interface contains only a single abstract method (such an interface is sometimes called a SAM type), it can be
* considered as a function, these interfaces can be implemented by using a lambda expression or a method reference.
*/
@FunctionalInterface
interface Func<T, R> {
// The annotation @FunctionalInterface is used to mark functional interfaces and to raise a compile-time error if
// an interface doesn't satisfy the requirements of a functional interface.
R apply(T val); // single abstract method
// The Func<T, R> interface meets the requirements to be a functional interface because:
// 1. it has a single apply abstract method
// 2. the method takes a value of type T and returns a result of the type R
// static and default methods are allowed in functional interfaces because these methods are not abstract
static void doNothingStatic() {
}
default void doNothingByDefault() {
}
}
class Functions {
public static long square(long val) {
return val * val;
}
}
public class FunctionalInterfaces {
public static void implementingFunctionalInterfaces() {
// 1. Anonymous classes
Func<Long, Long> square = new Func<Long, Long>() {
@Override
public Long apply(Long val) {
return val * val;
}
};
long value = square.apply(10L);
// 2. Lambda expression
square = val -> val * val;
// The code that creates a lambda expression may look as if the object of an interface type is created. As you
// know, it is impossible. Actually, the Java compiler automatically creates a special intermediate class that
// implements the functional interface and then creates an object of this class
// 3. Method reference
square = Functions::square;
}
public static void main(String[] args) {
Func<Integer, Integer> multiplier10 = val -> 10 * val;
System.out.println(multiplier10.apply(5)); // 50
}
}
class FunctionalInterfaceExample {
public static final TernaryIntPredicate ALL_DIFFERENT = (num1, num2, num3) ->
(num1 != num2) && (num1 != num3) && (num2 != num3);
@FunctionalInterface
public interface TernaryIntPredicate {
// Write a method here
boolean test(int num1, int num2, int num3);
}
class Seven {
public static SeptenaryStringFunction fun = (s1, s2, s3, s4, s5, s6, s7) ->
s1.toUpperCase() + s2.toUpperCase() + s3.toUpperCase() + s4.toUpperCase()
+ s5.toUpperCase() + s6.toUpperCase() + s7.toUpperCase();
}
@FunctionalInterface
interface SeptenaryStringFunction {
String apply(String s1, String s2, String s3, String s4, String s5, String s6, String s7);
}
}
##############################################
2. MethodReferences
import java.util.function.BiFunction;
import java.util.function.Function;
class Person {
String name;
public Person(String name) {
this.name = name;
}
}
public class MethodReferences {
public static void intro() {
// By method reference, we mean a function that refers to a particular method via its name and can be invoked
// any time we need it. The base syntax of a method reference looks like this: objectOrClass :: methodName
// where objectOrClass can be a class name or a particular instance of a class.
// This code works because the definition of the method int max(int a, int b) fits the type
// BiFunction<Integer, Integer, Integer>: they both mean taking two integer arguments and returning an integer value.
BiFunction<Integer, Integer, Integer> max = Integer::max;
// So, once assigned to an object, a method reference works in the same way as a lambda expression.
System.out.println(max.apply(2, 5)); // 5
// Here is an alternative way to create the same object using a lambda expression:
max = (x, y) -> Integer.max(x, y);
}
public static void kindsOfMethodReferences() {
// In general, there are four kinds of method references:
// 1. Reference to a static method: ClassName :: staticMethodName
Function<Double, Double> sqrt = Math::sqrt;
sqrt = x -> Math.sqrt(x);
// 2. Reference to an instance method of an object: objectName :: instanceMethodName
String whatsGoingOnText = "What's going on here?";
Function<String, Integer> indexWithinWhatsGoingOnText = whatsGoingOnText::indexOf;
System.out.println(indexWithinWhatsGoingOnText.apply("going")); // 7
System.out.println(indexWithinWhatsGoingOnText.apply("Hi")); // -1
indexWithinWhatsGoingOnText = x -> whatsGoingOnText.indexOf(x);
// 3. Reference to an instance method of an object of a particular type: ClassName :: instanceMethodName
Function<Long, Double> converter = Long::doubleValue;
converter.apply(100L); // the result is 100.0d
converter.apply(200L); // the result is 200.0d
converter = val -> val.doubleValue();
// 4. Reference to a constructor: ClassName :: new
Function<String, Person> personGenerator = Person::new;
Person johnFoster = personGenerator.apply("John Foster");
personGenerator = name -> new Person(name);
}
public static void main(String[] args) {
intro();
BiFunction<Integer, Integer, Integer> comparator = Integer::compare;
}
}
class MethodReferenceExercise {
}
###########################################
genericprogramming.essentials
1. GenericAndObject
class GenericType<T> {
private T t;
public GenericType(T t) {
this.t = t;
}
public T get() {
return t;
}
}
class NonGenericClass {
private Object val;
public NonGenericClass(Object val) {
this.val = val;
}
public Object get() {
return val;
}
}
public class GenericAndObject {
public static void reusingCodeWithGenerics() {
GenericType<String> instance1 = new GenericType<>("abc");
String str = instance1.get();
System.out.println(str); // abc
var instance2 = new GenericType<>((byte) 20);
System.out.println(instance2.get().getClass()); // class java.lang.Byte
}
public static void reusingCodeWithObject() {
// If we declare a field of type Object, we can assign a value of any reference type to it.
NonGenericClass instance2 = new NonGenericClass("abc");
System.out.println(instance2.get()); // abc
System.out.println(instance2.get().getClass()); // class java.lang.String
}
public static void theAdvantageOfGenerics() {
// After an invocation of the get() method we obtain an Object, not a String or an Integer. We cannot get a
// string directly from the method.
NonGenericClass instance2 = new NonGenericClass("abc");
// String str = instance2.get(); // Compile-time error: Incompatible types
String str = (String) instance2.get(); // "abc"
NonGenericClass instance3 = new NonGenericClass(123);
// str = (String) instance3.get(); // throws java.lang.ClassCastException
// Now we can see the main advantage of generics over the Object class. Because there is no need to perform an
// explicit typecast, we never get a runtime exception. A compile-time error will be detected by the programmer,
// not a user of the program.
GenericType<String> instance4 = new GenericType<>("abc");
str = instance4.get(); // There is no typecasting here
// Integer num = instance4.get(); // It does not compile
}
public static void genericsWithoutSpecifyingAType() {
GenericType instance5 = new GenericType("my-string");
// The above code is equivalent to the following line:
GenericType<Object> instance6 = new GenericType<>("abc"); // it is parameterized with Object
String str = (String) instance5.get();
str = (String) instance6.get();
var instance7 = new GenericType<>("xyz");
str = instance7.get();
}
public static void main(String[] args) {
// reusingCodeWithGenerics();
// reusingCodeWithObject();
Object val = "abc";
System.out.println(val.getClass());
}
}
###########################################
2. GenericMethods
/**
* All methods can declare their own type parameters, regardless of the class they belong to. This means that a
* non-generic class can contain generic methods.
* Static methods cannot use type parameters of their class! Type parameters of the class these methods belong to can
* only be used in instance methods. If you want to use type parameters in a static method, declare this method's own
* type parameters.
*/
class SimpleClass {
// Generic instance methods
public <T> T getParameterizedObject(T t) {
return t;
}
}
class GenericClass<T> {
// Because T was already declared in the class header, the method only has to declare the generic type U
public <U> T getParameterizedObject(T t, U u) {
return t;
}
}
public class GenericMethods {
// Generic static methods
public static <T> T doSomething(T t) {
return t;
}
public static <E> int length(E[] array) {
return array.length;
}
public static <E> void print(E[] array) {
for (E e : array) {
System.out.print(e + " ");
}
System.out.println();
}
public static <T, U> void method(T t, U u) {
// do something
}
public static void genericStaticMethod() {
int len = length(new Integer[]{1, 2, 3});
System.out.println(len);
String[] stringArray = { "a", "b", "c", "d" };
len = length(stringArray);
Character[] characters = { 'a', 'b', 'c' };
print(characters);
}
public static void genericInstanceMethod() {
SimpleClass instance = new SimpleClass();
Integer value = instance.getParameterizedObject(1999);
}
public static void main(String[] args) {
}
}
############################################
3. TypeBounds
public class TypeBounds {
/*
class Storage<T> {
private T nameOfElements;
//other methods
}
*/
// We would like to limit this class to be able to store only books.
static class Books { }
static class Brochures extends Books { }
static class Computers { }
static class Storage<T extends Books> {
private T nameOfElements;
//other methods
// Note that extends can mean not only an extension of a certain class but also an implementation of an
// interface. Generally speaking, this word is used as a replacement for an extension of normal classes, not
// generic classes. Trying to extend a generic class (for example, Storage<Brochures> extends Storage<Books>)
// will lead to an error.
}
public static void usage() {
Storage<Books> storage1 = new Storage<>(); //no problem
Storage<Brochures> storage2 = new Storage<>(); //no problem
// Storage<Computers> storage3 = new Storage<>(); //a compile-time error
}
public static void multipleBounds() {
// Note that under the hood, every type variable declared as a type parameter has a bound. If no bound is
// declared, Object is the bound:
// class SomeClass<T> {...} is equivalent to class SomeClass<T extends Object> {...}
// A type variable may have a single type bound: <T extends A>
// or have multiple bounds: <T extends A & B & C & ...>
// The first type bound ("A") can be a class or an interface. The rest of the type bounds ("B" onwards) must be
// interfaces.
// Note: if T has a bound that is a class, this class must be specified first! Otherwise, a compile-time error
// arises: <T extends B & C & A & ...> //an error if A is a class
}
public static void main(String[] args) {
}
}
#################################
4. Wildcards
import java.util.ArrayList;
import java.util.List;
/**
* Wildcards are a specific Java tool that allows the implementation of some compatibility between different generic
* objects. In essence, the wildcard is the "?" sign, used to indicate that a class, method, or field is compatible with
* different type parameters.
*/
public class Wildcards {
static class Book {}
static class Album extends Book {}
static class Booklet extends Book {}
static class AudioFile {}
public static void introWildcards() {
// As an object-oriented language, Java relies on the concept of inheritance. But since generics are type-safe
// structures, it is impossible to introduce inheritance for generic objects.
String str = "abc";
Object o = str;
List<Album> albums = new ArrayList<>();
// List<Book> books = albums; // compile-time error
// List<Album> is not a subclass of List<Book>: inheritance does not apply to generic classes.
// It doesn't matter that Album extends Book, because containers like List<T>, Set<T> and others are treated
// like independent classes.
// generic class or a method declared with wildcards can avoid inheritance issues.
List<? extends Book> albumsAndBooks1 = albums;
List<? super Album> albumsAndBooks2 = albums;
}
public static void upperBoundedWildcards() {
// Keyword: ? extends ReferenceType
// This code can be read as "any type that is a subtype of ReferenceType".
// In other words, if S is a subtype of T, then type List<S> is considered to be a subtype of List<? extends T>.
// This feature is known as covariance.
List<? extends Book> storage = new ArrayList<>();
List<Album> albums = new ArrayList<>();
storage = albums; // it works, Album is a subtype of Book
List<Booklet> booklets = new ArrayList<>();
storage = booklets; // it works, Booklet is a subtype of Book
List<AudioFile> recordings = new ArrayList<>();
// storage = recordings; // compile-time error, AudioFile is not a subtype of Book
// By using an upper-bounded wildcard, we made sure that the storage variable can only be set to subtypes of Book.
}
public void upperBoundedMethod(List<? extends Book> books) {
Book book = books.get(0); // It is fine
// books.add(new Album()); // compile-time error
// books.add(new Book()); // compile-time error
books.add(null); // also fine, because null is a special type-independent value
// Upper bounded wildcards are able to read content as a Book type, but they are not able to write any content
// except for a null value.
// Writing to a wildcard argument is prohibited to avoid runtime errors.
}
public static void lowerBoundedWildcards() {
// Keyword: ? super ReferenceType
// This means "any type that is a supertype of ReferenceType". For example, if S is a supertype of T, then
// List<S> is considered to be a supertype of List<? super T>. This feature is called contravariance.
List<? super Album> storage = new ArrayList<>();
List<Album> albums = new ArrayList<>();
storage = albums; // it works
List<Book> books = new ArrayList<>();
storage = books; // it works, Book is a supertype for Album
List<Booklet> booklets = new ArrayList<>();
// storage = booklets; // compile-time error, Booklet is not a supertype for Album
}
public void lowerBoundedMethod(List<? super Album> albums) {
Object object = albums.get(0); // it is ok. Object is upper bound of Album
// Book book = albums.get(0); // compile-time error
// Album album = albums.get(0); // compile-time error
// albums.add(new Object()); // compile-time error
// albums.add(new Book()); // compile-time error
albums.add(new Album()); // OK
albums.add(null); // OK, null is type-independent
// We can only be certain that it has type Object, since all classes inherit from Object.
// While Object is the only type that can be read from albums, Album is the only type that can be added.
}
public static void getAndPutPrinciple() {
// To decide whether extends or super should be used, it is worth remembering the Get and Put principle:
// 1. Use upper bounded wildcards when you only get values out of a structure (i.e. when you use only getters or
// similar methods).
// 2. Use lower bounded wildcards when you only put values into a structure (i.e. when you use only setters or
// similar methods).
// 3. Use Unbounded Wildcards (simply <?>) when you both get and put values (i.e. when you need to use both
// getters and setters).
// you can also use PECS: Producer Extends, Consumer Super.
// This means that if you get a value from a generic class, method, or any other object that produces what you
// need, you use extends.
// If you put or set a value into a generic class, method, or any other object that consumes what you put in,
// you use super.
}
public static void main(String[] args) {
}
}
class Example {
/*
public static void reverse(List<?> list) {
List<Object> tmp = new ArrayList<>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - i - 1)); // compile-time error
}
// The compiler signals an error because it does not know the type of object being written to the list.
// tmp.get(list.size() - i - 1) -> Object
}
*/
public static void reverse(List<?> list) {
reverseCaptured(list);
}
public static <T> void reverseCaptured (List<T> list) {
List<T> tmp = new ArrayList<>(list);
for (int i = 0; i < list.size(); i++) {
list.set(i, tmp.get(list.size() - 1 - i));
}
}
// test
public static void printList() {
List<String> strings = List.of("Hello", "World");
List<Integer> numbers = List.of(1, 2, 3, 4);
List<?> list = List.of(1, "Hello", true);
printUnboundedWildcardList(strings);
printUnboundedWildcardList(numbers);
printUnboundedWildcardList(list);
printGenericList(strings);
printGenericList(numbers);
printGenericList(list);
}
public static void printUnboundedWildcardList(List<?> list) {
for (Object element : list) {
System.out.print(element + " ");
}
System.out.println();
}
public static <T> void printGenericList(List<T> list) {
for (T element : list) {
System.out.print(element + " ");
}
System.out.println();
}
}
################################
oop.nestedclasses
1. InnerClass
/**
* You can create a class within another class, and such classes are called nested.
* A non-static nested class is called an inner class.
*/
// Both classesMagicCloak and Hammer are nested classes.
// The Superhero class is often called an outer class, and a nested class is called a member of an outer class.
class SuperHero {
class MagicCloak {
}
class Hammer {
}
}
class Cat {
private String name;
public Cat(String name) {
this.name = name;
}
private void sayMeow() {
System.out.println(this.name + " says: \"Meow\".");
}
public class Bow {
private String color;
public Bow(String color) {
this.color = color;
}
public void putOnABow() {
Cat.this.sayMeow();
System.out.println("Bow is on!");
}
public void printColor() {
System.out.println("Cat " + Cat.this.name + " has a " + this.color + " bow.");
}
}
}
public class InnerClass {
public static void innerClass() {
Cat cat = new Cat("Bob");
Cat.Bow bow = cat.new Bow("red");
bow.printColor();
}
public static void rulesOfInnerClass() {
// From inside the inner class, we can see all methods and fields of the outer class even if they are private.
// An inner class is associated with an instance of its enclosing class. So to instantiate an inner class and get
// access to it, you need to instantiate the outer class first
// if you make an inner class private, then it can only be accessed from inside the outer class. The same works
// for fields and methods.
// Prior to Java 16, inside an inner class, you could not define:
// 1. Any static members
// 2. Enums
// 3. An Interface
}
public static void main(String[] args) {
}
}
#############################################
2. NestedClass
/**
* Types of nested classes
* <p></p>
* <h>Nested Class</h>
* <ol>
* <li>Static nested class</li>
* <li>Non-static
* <ul>
* <li>Inner class</li>
* <li>Local inner class</li>
* <li>Anonymous inner class</li>
* </ul>
* </li>
* </ol>
*/
class Painting {
private String name;
private static double length;
private static double width;
public static void setLength(double length) {
Painting.length = length;
}
public static void setWidth(double width) {
Painting.width = width;
}
// Static nested class
public static class Sketch {
private int id;
public Sketch(int id) {
this.id = id;
}
public void drawSketch() {
drawForest();
drawBear();
}
private void drawForest() {
if (Painting.length > 5 && Painting.width > 3) {
System.out.println("Big forest was drawn in a sketch!");
} else {
System.out.println("Small forest was drawn in a sketch!");
}
}
private void drawBear() {
System.out.println("Bear was drawn in a sketch!");
}
}
}
class Outer {
private int number = 10;
void someMethod() {
final int x = 5;
class LocalInner {
private void print() {
System.out.println("x = " + x);
System.out.println("number = " + Outer.this.number);
}
}
LocalInner inner = new LocalInner();
inner.print();
}
}
public class NestedClass {
public static void staticNestedClass() {
Painting.Sketch sketch = new Painting.Sketch(0);
sketch.drawSketch();
// if you make a static nested class private, then it can only be accessed inside the outer class. The same
// works with fields and methods.
}
public static void localInnerClass() {
// You can define a local inner class inside any block. But, usually, local inner classes are defined inside a
// method body.
// Local inner class doesn't have an access modifier. Inside a local inner class you cannot define any static
// members, enums or interfaces.
Outer outer = new Outer();
outer.someMethod();
// Remember, a local inner class can be instantiated only within the block where the inner class is defined.
// Local variables must be declared as final or be effectively final, the latter means their value is never
// changed after initialization and there's no need for the keyword final.
}
public static void main(String[] args) {
}
}
#########################################
sourcestructure
MyModule
/**
* Package vs Module:
* <p></p>
* Packages group related classes. Usually, they are just folders in your source directory. They were introduced to keep
* related classes together, but they also serve the additional purpose of distinguishing between classes with the same
* name.
* Two additional points to keep in mind about Java packages:
* 1. Packages cannot be deployed by themselves. They contain the source code of an application, but the application
* still requires the JRE to run it.
* 2. Classes in a package can be accessed via reflection, even if the classes were declared private.
* <p></p>
* As for Java modules, it is helpful to think of them as large boxes that you put packages into. If the packages are
* similar or if they logically belong together because they contribute to one functionality, they can be bundled
* together into a Java module.
* In fact, starting from Java 9, the Java Platform API has been split into separate modules instead of one
* monolithic.jar file.
* <p></p>
* <p></p>
*
* Encapsulation
* <p></p>
* Java modules allow developers to choose which parts of a module's class files are visible to others and which parts
* are encapsulated. This enables the building of programs as loosely coupled modules, where developers interact only at
* the API level. By providing an interface and hiding the implementation, developers can easily update their codebase
* without affecting users or other developers.
* <p></p>
* <p></p>
*
* Dependency management
* <p></p>
* Java modules must contain a Module Descriptor file called module-info.java, which includes information such as the
* module's name, offered and consumed services, dependencies on other modules, explicitly available packages for other
* modules, and allowed reflection for other modules.
* This allows developers to explicitly define module dependencies and selectively control package accessibility and
* reflection.
* Prior to Java modules, developers relied on external build tools to manage dependencies, but now the JVM can detect
* missing dependencies during startup and throw an error.
* <p></p>
* <p></p>
*
* Modules in action
* <p></p>
* The name of our module must be unique. This is why you often see both packages and modules prefixed with a company
* domain before their names. For example, if your module is named JsonUtil, there's a fair chance someone else in the
* world has used that name already or will try to do so in the future.
*
* We can declare our module's dependencies inside this declaration using "requires". If we want to make parts of our
* module available for other modules to use, we must explicitly do so using the "exports" keyword.
*/
public class MyModule {
public static void main(String[] args) {
System.out.println("This is package javacore.organization.sourcestructure");
}
}
/*
module-info.java
module com.myCompany.myModuleName {
exports javacore.organization.sourcestructure; // package nay duoc cong khai va co the su dung boi cac module khac
requires javafx.graphics;
}
*/
Editor is loading...
Leave a Comment