Untitled
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; } */
Leave a Comment