Untitled

 avatar
unknown
plain_text
a year ago
28 kB
7
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;
}


 */
Leave a Comment