Python equivalent in Java for bilingual developers
Table of contents.
-
Data structures
a) Lists
b) Tuples
c) Sets
d) Maps and dictionaries
e) List comprehension
f) Popular data structures
-
General programming
a) Method signatures & type hints
b) Lambda expressions
c) Simple file read / write
d) Basic HTTP client
e) Errors & exceptions handling
f) Generics
-
OOP
a) Objects encapsulation
b) Inner classes
c) Multiple inheritance
d) Singleton patterns
e) Polymorphism example
-
Decorators & design patterns
a) Decorator and monkey patching
b) Builder pattern
mlist: List[str] = ["A", "B", "C"]
mlist.insert(4, "D") # insert at an index 4
mlist.sort() # in place sort
mlist.remove("C") # remove first "C" element
popped: str = mlist.pop(2) # remove the item at index 2
mlist.reverse() # reverse the mlist
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add(3,"D");
Collections.sort(list); //sort
list.remove("C"); //remove first "C" element
list.remove(2); //remove object at the index 2
Collections.reverse(list); //reverse the list
int n = list.indexOf("A"); //return the index of first occurrence of "A"
}
}
words: Tuple[str] = ("A", "B") # or words = "ABng"
print("A" in words) # True
print(hash(words)) # tuples are hashable
a,b = words # or a,b = "A", "B"
print(a) # Good
print(b) # Morning
print(len(words)) # 2
Tuple is just kind of immutable list. There are many ways how to use immutable collections of any kind e.g. Map, List, Set in Java.
a) Older approach (JDK 5)
public class Main {
public static void main(String[] args) {
// Example (immutable list)
List<String> list = new ArrayList<String>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = Collections.unmodifiableList(list);
}
}
b) Newer approach (JDK 9 factory methods)
public class Main {
public static void main(String[] args) {
List<String> immutableList = List.of("A", "B");
Set<String> immutableSet = Set.of("A", "B");
Map<String, String> immutableMap = Map.of("key1", "Value1", "key2", "Value2", "key3", "Value3");
}
}
c) Google Guava approach An "Immutable collection" can be created in several ways:
-
using the copyOf method, for example, ImmutableSet.copyOf(set)
-
using the of method, for example, ImmutableSet.of("a", "b", "c") or ImmutableMap.of("a", 1, "b", 2)
-
using a Builder, for example:
public static final ImmutableSet<Color> GOOGLE_COLORS = ImmutableSet.<Color>builder() .addAll(WEBSAFE_COLORS) .add(new Color(0, 191, 255)) .build();
public class Main {
public static void main(String[] args) {
// Example 1
List<String> list = new ArrayList<String>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = ImmutableList.copyOf(list);
// Examle 2 (immutable list via builder)
List<String> list = new ArrayList<String>(Arrays.asList("one", "two", "three"));
ImmutableList<Object> unmodifiableList = ImmutableList.builder().addAll(list).build();
}
}
Possible classes:
general schema: Immutable[XXX] examples:
- ImmutableCollection
- ImmutableList
- ImmutableSet
- ImmutableSortedSet
- ImmutableMap
- ImmutableSortedMap
- ImmutableMultiset
- ImmutableSortedMultiset
- ImmutableMultimap
- ImmutableListMultimap
- ImmutableSetMultimap
- ImmutableBiMap
- ImmutableClassToInstanceMap
- ImmutableTable
See more: https://github.com/google/guava/wiki/ImmutableCollectionsExplained
d) Apache Commons approach
public class Main {
public static void main(String[] args) {
// Example 1 (ListUtils)
List<String> list = new ArrayList<String>(Arrays.asList("one", "two", "three"));
List<String> unmodifiableList = ListUtils.unmodifiableList(list);
// Example 2 (Immutable classes from org.apache.commons.lang3.tuple)
ImmutablePair<String, List<Integer>> immutablePair = new ImmutablePair<>("A", Arrays.asList(1,2,3));
ImmutableTriple<String, Integer, Character> immutableTriple = new ImmutableTriple<>("A", 2, 'x');
}
}
e) Java Tuples Library approach (see https://www.javatuples.org/)
public class Main {
public static void main(String[] args) {
// Pair via static factory
Pair<String, Integer> pair = Pair.with("A", 12);
// Pair via constructor
Pair<String, Integer> person = new Pair<String, Integer>("A", 12);
// Quartet from collections
List<String> listOf4Names = Arrays.asList("A1","A2","A3","A4");
Quartet<String, String, String, String> quartet = Quartet.fromCollection(listOf4Names);
// triplet
Triplet<String, String, String> triplet = Triplet.with("Java", "C", "C++");
}
}
set_of_words1: Set[str] = {"A", "B", "C"}
# example2
set_of_words2: Set[str] = set()
set_of_words2.add("A")
set_of_words2.add("B")
set_of_words2.add("C")
element_is_in_set: bool = ("Boat" in set_of_words2) # True
print(set_of_words2.intersection(set_of_words1)) # intersection
print(set_of_words2.union(set_of_words1)) # union
public class Main {
public static void main(String[] args) {
Set<String> set1 = new HashSet<>();
Set<String> set2 = new HashSet<>() {{ add("A"); add("B");}};
set1.add("A");
set1.add("B");
set1.add("C");
//intersection
Set<String> intersect = new HashSet<>(set1);
intersect.retainAll(set2);
//union
Set<String> union = new HashSet<>(set1);
union.addAll(set2);
}
}
phonebook: Dict[str, str] = {}
phonebook = {"Slawek": "122-99981", "Mark": "333-1111", "cAdam": "1222-112213"}
print(phonebook["Slawek"]) # value of a key
phonebook.keys() # List of keys
phonebook.values() # List of values
element_is_in_dict = "Slawek" in phonebook # True
(phonebook.items()) # prints tuples of key-value pairs
public class Main {
public static void main(String[] args) {
HashMap<String, String> phoneBook = new HashMap<String, String>();
phoneBook.put("Slawek", "111-1111");
phoneBook.put("Lucy", "5255-2222");
phoneBook.put("Mark", "51155-3333");
//looping elements
Map<String, String> map = new HashMap<String, String>();
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("Key = " + entry.getKey() +
", Value = " + entry.getValue());
}
//get key value
phoneBook.get("Slawek");
//get all key
Set keys = phoneBook.keySet();
//get number of elements
phoneBook.size();
//delete all elements
phoneBook.clear();
//delete an element
phoneBook.remove("Slawek");
}
}
from typing import *
# Example 1
list_using_comp: List[int] = [var ** 2 for var in range(1, 10) if var % 2 == 0]
# [4, 16, 36, 64]
# Example 2
char_list: List[chr] = ['', ' a', 'b', '\t']
[x.strip() for x in char_list if x.strip()]
# ['a', 'b']
# Example 3
nums: List[int] = [1, 2, 3, 4]
fruit: List[str] = ["Apples", "Peaches", "Pears", "Bananas"]
[(i, f) for i in nums for f in fruit if f[0] == "P" if i % 2 == 1]
# [(1, 'Peaches'), (1, 'Pears'), (3, 'Peaches'), (3, 'Pears')]
in Java there is not simple equivalent for Python's syntax. We can use Java Streams (Java 8+), additional Java libraries e.g. https://github.com/farolfo/list-comprehensions-in-java
public class Main {
public static void main(String[] args) {
// Example 1
List<Integer> integers =
// list of integers from range
IntStream.range(0, 10).boxed()
.filter(n -> n % 2 == 0)
.map(n -> Math.pow(n, 2)).map(Double::intValue)
.collect(Collectors.toList());
// [0, 4, 16, 36, 64]
// Example 2 (https://github.com/farolfo/list-comprehensions-in-java)
Predicate<Integer> even = x -> x % 2 == 0;
List<Integer> evens = new ListComprehension<Integer>()
.suchThat(x -> {
x.belongsTo(Arrays.asList(1, 2, 3, 4));
x.is(even);
});
// evens = {2,4};
}
}
Python Data Structures - https://docs.python.org/3/tutorial/datastructures.html
Collection interface - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Collection.html
Set - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Set.html
List - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/List.html
Map - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Map.html
SortedSet - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/SortedSet.html
SortedMap - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/SortedMap.html
HashSet - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/HashSet.html
TreeSet - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/TreeSet.html
ArrayList - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/ArrayList.html
LinkedList - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/LinkedList.html
Vector - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Vector.html
Collections - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Collections.html
AbstractCollection - https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/AbstractCollection.html
Example 1
from typing import *
# function wirh some positional params
def test_function(x: int, y: int, w: int = 0, h: int = 0) -> None:
print("x: %s, y: %s, w: %s, h: %s" % (x, y, w, h))
# normal function call
test_function(1, 2, 3, 4);
# x: 1, y: 2, w: 3, h: 4
# arguments unpacking function call
arg_tuple: Tuple[int] = (1, 2)
arg_dictionary: Dict[str, int] = {'w': 20, 'h': 10}
test_function(*arg_tuple, **arg_dictionary)
# x: 1, y: 2, w: 20, h: 10
Example 2
def test_function2(arg1: List[Any], *args: int, **kwargs: str) -> None:
print(arg1)
for x in args:
print("next arg is: " + str(x))
print(kwargs)
print(kwargs.get("this"), kwargs.get("that"))
# another call with explicit parameters
test_function2([1, 2, "three"], 4, 5, 6, this="this v", that="that v")
# [1, 2, 'three']
# next arg is: 4 next arg is: 5 next arg is: 6
# {'this': 'this', 'that': 'that'}
# this v that v
# arguments unpacking example
targs: List[int] = [4, 5, 6]
targsb: Tuple[int] = (4, 5, 6, 6)
kwargs: Dict[str, str] = {"this": "this v", "that": "that v"}
test_function2([], *targs, **kwargs)
test_function2([], *targsb, **kwargs)
# the same result as in above example
Example 3
def test_function2(arg1: List[Any], *args: int, **kwargs: str) -> None:
print(arg1)
for x in args:
print("next arg is: " + str(x))
print(kwargs)
print(kwargs.get("this"), kwargs.get("that"))
# another call with explicit parameters
test_function2([1, 2, "three"], 4, 5, 6, this="this v", that="that v")
# [1, 2, 'three']
# next arg is: 4 next arg is: 5 next arg is: 6
# {'this': 'this', 'that': 'that'}
# this v that v
# arguments unpacking example
targs: List[int] = [4, 5, 6]
targsb: Tuple[int] = (4, 5, 6, 6)
kwargs: Dict[str, str] = {"this": "this v", "that": "that v"}
test_function2([], *targs, **kwargs)
test_function2([], *targsb, **kwargs)
# the same result as in above example
More examples:
# type aliases
from typing import (Any,
Callable,
Dict,
Tuple,
TypeVar)
T = TypeVar('T', int, float, complex)
Vector = Iterable[Tuple[T, T]]
def inproduct(v: Vector[T]) -> T:
return sum(x * y for x, y in v)
def dilate(v: Vector[T], scale: T) -> Vector[T]:
return ((x * scale, y * scale) for x, y in v)
# generics
T = TypeVar('T') # Declare type variable
def first(l: Sequence[T]) -> T: # Generic function
return l[0]
# iterable
def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None:
for var in vars:
var.set(0)
# union types
def handle_employees(e: Union[Employee, Sequence[Employee]]) -> None:
if isinstance(e, Employee):
e = [e]
# callale
AnyCallable = Callable[..., Any]
# type var
Domain = TypeVar('Domain')
# map
Operator = Map[Domain, Domain]
# tuple with strings only (no matter how many)
Keys = Tuple[str, ...]
# tuple with Domain type objects
Types = Tuple[Domain , ...]
# dict
Registry = Dict[int, Dict[Types, Dict[Keys, Dict[Types, AnyCallable]]]]
Example 1
@Data
class CarAnother extends Vehicle {
/**
* doSomething - method with many params of many kinds
*
* @param a
* @param b
* @param strObject
* @param car
* @param vehicles
* @param anything
*/
public void doSomething(int a, char b, String strObject, Car car, List<Vehicle> vehicles, Object anything) {
a += 1;
strObject.trim();
car.setName("Newname");
vehicles.stream().forEach(Vehicle::drive);
}
/**
* doSomething - method with many params - collections and arrays
*
* @param vehicles
* @param colOfVehicles
* @param integers
* @param cars
*/
public void doSomething(Set<Vehicle> vehicles, Collection<? extends Vehicle> colOfVehicles, int[] integers, Car[] cars) {}
/**
* doSomethingOnSetsOfVehicles - varargs of sets of vehicles
*
* @param setsOfVehicles
*/
public void doSomethingOnSetsOfVehicles(Set<Vehicle> ... setsOfVehicles) {
for (Set<Vehicle> setv : setsOfVehicles) {
System.out.println(setv.toArray());
}
}
/**
* doSomethingOnManyStrings - varargs with strings
*
* @param str
*/
public void doSomethingOnManyStrings(String ...str) {
String res = Arrays.stream(str)
.map(String::trim)
.collect(Collectors.joining(" "));
System.out.println(res);
}
/**
* joinDictionaries - join dictionaries, no matter how many
* here we have varargs of maps and filter predicate as a positional parameter
*
* @param filter
* @param dicts
*/
public void joinDictionaries(Predicate<Map<String, Integer>> filter, Map<String, Integer>...dicts) {
Map<String, Integer> mergedMap = Arrays.stream(dicts).filter(filter)
.map(Map::entrySet)
.flatMap(Collection::stream)
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
Integer::max
)
);
System.out.println(mergedMap);
}
}
Test
public class Main {
public static void main(String[] args) {
CarAnother carAnother = new CarAnother();
// Example 1 - varargs
carAnother.doSomethingOnManyStrings("one", "two", "three", "four"); // one two three four
// the same function but more params
carAnother.doSomethingOnManyStrings("one", "two", "three", "four", "five", "six"); // one two three four five six
// Example 2 - varargs of sets
Map<String, Integer> dict1 = new HashMap<String, Integer>() {{
put("key1", 1);
put("key2", 2);
}};
Map<String, Integer> dict2 = new HashMap<String, Integer>() {{
put("key3", 3);
put("key4", 4);
}};
Map<String, Integer> dict3 = new HashMap<String, Integer>() {{
put("key5", 5);
}};
// run on 2 dictionaries
carAnother.joinDictionaries(dict -> dict.size() > 0, dict1, dict2); // {key1=1, key2=2, key3=3, key4=4}
// run on 3 dictionaries (no matter how many dictionaries we want to use - it will work properly)
carAnother.joinDictionaries(dict -> dict.size() > 0, dict1, dict2, dict3); // {key1=1, key2=2, key5=5, key3=3, key4=4}
List<Vehicle> vehicles = new ArrayList<>();
Set<Vehicle> uniqueVehicles = new HashSet<>(vehicles);
Car[] cars = new Car[2];
// run method with mane different params
carAnother.doSomething(1, 'x', "str", new Car("",""), vehicles, null);
// run method with mane different params (collections and arrays)
carAnother.doSomething(uniqueVehicles, vehicles, new int[]{1,2,3}, cars);
}
}
Simulateing Python's Dictionary parameters.
@Data
class CarAnother extends Vehicle {
/**
* doSomethingOnDictParams
*
* @param normalPositionalParam - normal integer param on first position
* @param dict - map/dictionary
* @param strParams - vararg (we can put here many strings)
*/
public void doSomethingOnDictParams(int normalPositionalParam, Map<String, String> dict, String ...strParams) {
String res = Arrays.stream(strParams).collect(Collectors.joining(" "));
System.out.println(normalPositionalParam);
System.out.println(dict);
System.out.println(res);
}
}
Testing with few dictionary creation approaches.
public class Main {
public static void main(String[] args) {
CarAnother carAnother = new CarAnother();
// dict, style 1
Map<String, String> myDict = new HashMap<>();
myDict.put("key1", "val1");
myDict.put("key2", "val2");
// dict, style 2
Map<String, String> myDict2 = Stream.of(new String[][] {
{ "key1", "val1" },
{ "key2", "val2" },
}).collect(Collectors.toMap(data -> data[0], data -> data[1]));
// dict, style 3
Map<String, String> myDict3 = Map.of(
"key1", "val1",
"key2", "val2"
);
// dict, style 4
Map<String, String> myMap = new HashMap<>() {{
put("key1", "val1");
put("key2", "val2");
}};
// testing
carAnother.doSomethingOnDictParams(12, myDict,"a");
carAnother.doSomethingOnDictParams(12, myDict2,"a");
carAnother.doSomethingOnDictParams(12, myDict3,"a");
carAnother.doSomethingOnDictParams(12, myDict, "a", "b", "c");
carAnother.doSomethingOnDictParams(12, myDict, "a", "b", "c", "d", "e");
}
}
Basic examples of lambda expression
from functools import reduce
from typing import Tuple, Dict, Any
import math
## Example 1
# normal function
def cube(y):
return y * y * y;
# lambda version of the same function
cube_lambda = lambda x: x * x * x
print(cube_lambda(5)) # 125
## Example 2
square_root = lambda x: math.sqrt(x)
print(square_root(16)) # 4.0
## Example 3
power_sum = lambda *a, **b: list(map(lambda x: x ** b.get("power", 1) * b.get("multiplier", 1) % b.get("modulo", 1), a))
res = power_sum(1, 2, 3, power=2, multiplier=2, modulo=100)
print(res)
Mappers, Filters, Reducers
from functools import reduce
from typing import Dict, Any
from typing import Tuple
## Example 4
my_list = [1, 5, 6, 9, 11, 14, 15, 16]
my_list = list(filter(lambda x: (x % 2 == 0), my_list))
# Output: [4, 6, 8, 12]
print(my_list)
# 125
# 4.0
# [2, 8, 18]
# [6, 14, 16]
### Filter
mylist = [5, 7, 22, 97, 54, 62, 77, 23, 73, 61]
mylist_result = list(filter(lambda x: (x % 2 == 0), mylist))
print(mylist_result) # [22, 54, 62]
### Mapper
items = [1, 2, 3, 4, 5]
def sqr(x): return x ** 2
list(map(sqr, items))
### Reducer example
my_list = [15, 81, 10, 20, 52, 100]
sum = reduce((lambda x, y: x + y), my_list)
print(sum) # 278
# another reducer
res = reduce(lambda x, y: x + y, [1, 2, 3, 4, 5, 6])
print(res) # 21
## Filter and mapper
words = ["my", "passion", "is", "hehe", "programming", "and", "finding", "new", "haha", "great", "ways"]
forbidden_words = ["hehe", "haha"]
final_list = " ".join(map(lambda y: y.upper(), filter(lambda x: not forbidden_words.__contains__(x), words)))
print(final_list)
# MY PASSION IS PROGRAMMING AND FINDING NEW GREAT WAYS
# Another filter and mapper example
class Amount:
default_rates: Dict[str, float] = {
"EUR": 1.0,
"RON": 5.2,
"PLN": 4.5
}
def __str__(self) -> str:
return "amount: {}, curency: {}".format(self.amount, self.curency)
def __init__(self, amount: float, curency: str) -> None:
self.amount = amount
self.curency = curency
def calcAmountInEur(self) -> float:
return self.amount * Amount.default_rates
@staticmethod
def get_big_wins_in_eur(wins: Tuple["Amount",...]) -> Any:
mappedToEur = map(lambda x: Amount(x.amount * Amount.default_rates.get(x.curency), "EUR"), wins).__iter__()
bigwin = lambda x: x.amount > 100
return list(filter(bigwin, mappedToEur))
list_of_wins: Tuple[Amount, ...] = (Amount(110, "EUR"), Amount(25, "EUR"), Amount(520, "RON"), Amount(30, "PLN"))
res: Tuple[Amount, ...] = Amount.get_big_wins_in_eur(list_of_wins)
print(list(map(lambda x: str(x), res)))
# ['amount: 110.0, curency: EUR', 'amount: 2704.0, curency: EUR', 'amount: 135.0, curency: EUR']
# Example - mapping dictionaries with lambda expressions
mydict = [{'name': 'english', 'rate': 10}, {'name': 'german', 'rate': 8}]
mydict_r1 = map(lambda x: x['name'], mydict)
print(*mydict_r1) # english german
mydict_r2 = map(lambda x: x['rate'] * 10, mydict)
print(*mydict_r2) # 100 80
mydict_r3 = map(lambda x: x['name'] == "german", mydict) # Output: [True, False]
print(*mydict_r3) # False True
## More examples
# sort list
cities = ["New York", "Tokyo", "Warsaw", "London"]
print(sorted(cities, key = lambda k: k.casefold())) # ['London', 'New York', 'Tokyo', 'Warsaw']
nums = [-1, 14, -29, 33]
print(sorted(nums, key = lambda n: n)) # [-29, -1, 14, 33]
# sorting list of tuples
def order_by_amount(point):
return point.amount
listofw: Tuple[Amount, ...] = (Amount(110, "EUR"), Amount(25, "EUR"), Amount(520, "RON"), Amount(30, "PLN"))
res = sorted(listofw, key=order_by_amount)
print(list(map(lambda x: str(x), res)))
# ['amount: 25, curency: EUR', 'amount: 30, curency: PLN', 'amount: 110, curency: EUR', 'amount: 520, curency: RON']
Basic lambda expressions in Java.
package tests;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.*;
/**
* Functional interface
*/
interface Testable {
int test(String a, Student b);
}
class Index {
int num;
Index(int num){
this.num = num;
}
int getIndexNumber(){
return num;
}
}
// example Student object
// Lombok
@Data
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Optional<Index> getIndex() {
return Optional.of(new Index(331));
}
}
class Operations {
public static void consumeStudents(List<Student> students, Function<Student, String> function, Consumer<String> consumer) {
for (Student s : students) {
consumer.accept(function.apply(s));
}
}
public static List<Student> filterStudents(Supplier<List<Student>> supplier, Predicate<Student> predicate) {
List<Student> result = new ArrayList<>();
List<Student> students = supplier.get();
for (Student s : students) {
if (predicate.test(s)) { //sprawdz wzgledem predykatu Predicate i jak OK to OK
result.add(s);
}
}
return result;
}
public static List<Student> createData() {
List<Student> result = new ArrayList<>();
result.add(new Student("Adam", 31));
result.add(new Student("John", 29));
return result;
}
public static void testujStatycznaFunkcje(Student student) {
System.out.println("to jest nasz consumer");
}
public static void testujFuncReference(Consumer<Student> consumer) {
consumer.accept(new Student("Billy", 32));
}
}
public class App {
public static void main(String[] args) {
// ########## Example 1
// classic anonymous class on Runnable
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("runnable");
}
};
// lambda version
Runnable r2 = () -> {
System.out.println("runnable");
};
// ##### Example 2
// classic comparable
Comparable<String> comp1 = new Comparable<String>() {
@Override
public int compareTo(String o) {
return 0;
}
};
// lambda version
Comparable<String> comp2 = o -> 0;
// ##### Example 3
// lambda expression based on custom functional interface
Testable testElem = (String a, Student stnd) -> {
return 123;
};
// ##### Example 4 - Predicate, Consumer, Supplier, Function
Predicate<Student> over30 = student -> student.getAge() > 30;
Predicate<Student> nameNotAdam = student -> student.getName() != "Adam";
Predicate<Student> nameNoIvan = student -> student.getName() != "Ivan";
// join predicates
Predicate<Student> polaczonaWartoscWieluPred = over30.and(nameNotAdam).or(nameNoIvan).negate();
// Consumer example
Consumer<String> print = text -> System.out.println(text);
Consumer<String> changeName = s -> {
System.out.println(s + "-superman");
};
// joining consumers together
Consumer<String> lancuchKonsumerow = print.andThen(changeName);
// Supplier example
Supplier<List<Student>> supplyPredefinedStudents = () -> Operations.createData();
// Function
Function<Student, String> getStudentName = student -> student.getName();
// Bi Function
BiFunction<Student, String, Integer> obrabiajZwrocWiek = (student, co) -> {
if (co == "wiek") return student.getAge();
return 20;
};
// BinaryOperator - classic anonymous class based on interface
BinaryOperator<Student> wspolnyKolega = new BinaryOperator<Student>() {
@Override
public Student apply(Student student, Student student2) {
return new Student("Ann", 23);
}
};
// lambda version
BinaryOperator<Student> wspolnyKolegaLambda = (student, student2) -> new Student("Ann", 23);
// ##### Example 5 - lambda expresssions based on primitive predicates
IntPredicate predInt2 = new IntPredicate() {
@Override
public boolean test(int value) {
return false;
}
};
IntSupplier is132 = () -> 0;
IntConsumer ic134 = value -> {
};
IntFunction if136 = value -> null;
IntSupplier is1322 = () -> 0;
IntConsumer ic1343 = value -> {
};
IntFunction if1362 = value -> null;
// ##### Example 6 - method's references
Consumer<Student> consStud = (Student s) -> Operations.testujStatycznaFunkcje(s);
Consumer<Student> consStud2 = Operations::testujStatycznaFunkcje;
Consumer<String> print179 = System.out::println;
Operations.testujFuncReference(Operations::testujStatycznaFunkcje);
Supplier<List<Student>> supplyPredefinedStudents4 = Operations::createData;
Function<Student, String> getStudentName192 = Student::getName;
Supplier<List<Student>> supplyPredefinedStudents210 = Operations::createData;
// ##### Example 7 - Optional
Student student = new Student("Ann", 23);
Optional<Index> index = student.getIndex();
if (index.isPresent()) {}
try {
Index indexOrThrow = student.getIndex().orElseThrow(() -> new Exception("No Index"));
Index indexOrNull = student.getIndex().orElseGet(null);
index.ifPresent(i -> {
System.out.println("index is fine");
});
} catch (Exception e) {
e.printStackTrace();
}
index.orElse(new Index(1222));
// chaining map, filter and optional
index
//map
.map(i -> i.getIndexNumber())
//filter
.filter(indexNumber -> indexNumber.equals("213"))
//handle optional
.ifPresent(indexNumber -> System.out.println(indexNumber));
// chaining filter and supplied
Operations.consumeStudents(
Operations.filterStudents(supplyPredefinedStudents, over30), getStudentName, print
);
}
}
Filters, transformers, mappers, generators.
import lombok.Data;
import java.util.*;
import java.util.function.*;
class Index {
int num;
Index(int num){
this.num = num;
}
int getIndexNumber(){
return num;
}
}
@Data
class Student {
private String name;
private int age;
public Student() {
this.name = "no name";
this.age = 20;
}
public Student(String name) {
this.name = name;
this.age = 0;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Optional<Index> getIndex() {
return Optional.of(new Index(123));
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
/* custom functional interfaces */
@FunctionalInterface
interface MyProcessor<T>{
T process(T element);
}
@FunctionalInterface
interface FilterInter<S>{
boolean testMatchB(S element);
}
@FunctionalInterface
interface MyTransformer<T,S>{
T transform(S v);
}
@FunctionalInterface
interface Operator<R,T,S>{
R oper(T v1, S v2);
}
class FilterTransformerGenerator {
static <T> List<T> findAll(Collection<T> src, Predicate<T> p) {
List<T> trg = new ArrayList<>();
for(T e : src) if(p.test(e)) trg.add(e);
return trg;
}
static <S,T> List<T> transform(Collection<S> src, Function<S,T> f) {
List<T> trg = new ArrayList<>();
for(S e : src) trg.add(f.apply(e));
return trg;
}
static <T> List<T> generate(int n, Supplier<T> s) {
List<T> trg = new ArrayList<>();
for(int i = 0; i < n; i++){
trg.add(s.get());
}
return trg;
}
static <T> void process(Collection<T> src, Consumer<T> c) {
for(T e : src) c.accept(e);
}
public static <T,S> List<T> create(List<S> src, MyTransformer<T,S> t){
List<T> target = new ArrayList<>();
for(S s : src){
target.add(t.transform(s));
}
return target;
}
public static <R,T,S> List<R> create(List<T> src1, List<S> src2, Operator<R,T,S> o){
List<R> res = new ArrayList<>();
for(int i = 0; i < src1.size(); i++){
res.add(o.oper(src1.get(i), src2.get(i)));
}
return res;
}
public static FilterInter<String> funcDecorator(int argument){
return (String x) -> argument == 1 && x == "show_only_that";
}
static <T> T sConverter(T elementAlboCos, MyProcessor<T> funkcja){
return funkcja.process(elementAlboCos);
}
}
public class Main {
public static void main(String[] args) {
List<Student> t = FilterTransformerGenerator.generate(10, Student::new);
List<Student> out = FilterTransformerGenerator.findAll(t, e -> e.getName() == "Adam");
FilterTransformerGenerator.process(out, e->e.setName(e.getName() == "no name" ? "John Doe" : e.getName()));
List<String> eett = FilterTransformerGenerator.transform(t, Student::getName);
Predicate<Student> mojPredykat1 = e -> e.getName() == "Marc";
Predicate<Student> mojPredykat2 = e -> e.getName() != "Ivan";
Predicate<Student> mojPredykat3 = e -> e.getName() != "Adam";
// chained predicates
List<Student> out2 = FilterTransformerGenerator.findAll(t, mojPredykat1
.and(mojPredykat2)
.or(mojPredykat3)
.negate()
);
// compose operators
UnaryOperator<String> italic = e -> "<italic>" + e + "</italic>" ;
UnaryOperator<String> bold = e -> "<bold>" + e + "</bold>" ;
Function<String,String> kompoz1 = italic.compose(bold);
Function<String,String> kompoz2 = italic.andThen(bold);
String s5666 = "Abc def ghi";
kompoz1.apply(s5666); //<bold><italic>Abc def ghi</italic></bold>
kompoz2.apply(s5666); //<italic><bold>Abc def ghi</bold></italic>
//Konsumery [Consumer, BiConsumer]
Consumer<Student> podwyzszNumer = e -> e.setName("No name");
Consumer<Student> podizelPrzez10 = e -> e.setAge(e.getAge() - 1);
podwyzszNumer.andThen(podizelPrzez10);
// creators
List<String> s = Arrays.asList("dog", "cat", "fish");
List<String> sn = Arrays.asList("111", "222", "333");
List<Integer> pos = Arrays.asList(1,3);
List<String> outtest = FilterTransformerGenerator.create(s, e -> e.toUpperCase());
// or
List<String> outtest2 = FilterTransformerGenerator.create(s, String::toUpperCase);
List<Integer> l2 = FilterTransformerGenerator.create(sn, e -> Integer.parseInt(e));
// or
List<Integer> l3 = FilterTransformerGenerator.create(sn, Integer::parseInt);
List<Student> l4 = FilterTransformerGenerator.create(s, e -> new Student("d"));
// or
List<Student> l5 = FilterTransformerGenerator.create(s, Student::new);
String tekst_a = "dog and cat are at home";
List<Integer> l6 = FilterTransformerGenerator.create(s, e -> tekst_a.indexOf(e));
// or
List<Integer> l7 = FilterTransformerGenerator.create(s, tekst_a::indexOf);
// 3-args create
List<String> l8 = FilterTransformerGenerator.create(s, pos, (e1, e2) -> e1.substring(e2));
// or
List<String> l9 = FilterTransformerGenerator.create(s, pos, String::substring);
FilterInter<String> myFunc = FilterTransformerGenerator.funcDecorator(12);
myFunc.testMatchB("aba"); // false
myFunc.testMatchB("show_only_that"); // true
Integer intres = FilterTransformerGenerator.sConverter(10, n -> n * n * n);
String strres = FilterTransformerGenerator.sConverter(
"My String", n -> n + " add this ");
}
}
Popular functional interfaces in Java API.
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
public static void main(String[] args) {
/* A) ActionListener */
javax.swing.Timer timer5400 = new javax.swing.Timer(1000, e -> {
//do something
});
/* B) FileFilter, method: actionPerformed */
java.io.File[] files3408 = new java.io.File("/dir").listFiles(
f -> f.isFile()
&& f.getName().endsWith(".php")
&& f.lastModified() >= 1234567.12
);
/* C) Comparator (compare) */
List<String> myList = Arrays.asList("abcd", "efg", "hijk");
Collections.sort(myList, (s1, s2) -> s1.length() - s2.length());
SortedSet<String> testA5 = new TreeSet<>(
(s1, s2) -> {
return 456;
}
);
/* D) Runnable - run() */
Future<?> ftask = Executors.newSingleThreadExecutor().submit(
() -> {
int abc = 1;
}
);
/* E) Callable - call() */
ExecutorService ex = Executors.newCachedThreadPool();
Future<String> task = ex.submit(
() -> new String("xxx")
);
}
}
# read line by line
f = open("/example.txt","r")
for line in f:
print(line)
f.close()
# read the entire file in memory
f = open("/example.txt","r")
contents = f.read()
print(contents)
f.close()
# write to a file
f = open("/example.txt","r+") # open for read and write at the same time
f.write("test content")
contents = f.read()
print(contents) # test content
Approach with Buffered input and output stream
public class Main {
public static void main(String[] args) {
String path = "/example.txt";
// write the content in file
try(BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(path))) {
String fileContent = "This is a sample text.";
bufferedOutputStream.write(fileContent.getBytes());
} catch (IOException e) {
// ...
}
// read the content from file
try(BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(path))) {
int ch = bufferedInputStream.read();
while(ch != -1) {
System.out.print((char)ch);
ch = bufferedInputStream.read();
}
} catch (FileNotFoundException e) {
// ...
} catch (IOException e) {
// ...
}
}
}
Apache Commons FileUtils approach
public class Main {
public static void main(String[] args) throws IOException {
// READ
String path = "/example.txt";
File myfile = new File(path);
String contents = FileUtils.readFileToString(myfile, StandardCharsets.UTF_8.name());
System.out.println(contents);
List<String> lines = FileUtils.readLines(myfile, StandardCharsets.UTF_8.name());
// WRITE
String string = "Sample text";
File myfile = new File(path);
FileUtils.writeStringToFile(myfile, string, StandardCharsets.UTF_8.name());
}
}
import requests
r = requests.get("https://google.com/example_data")
print(r.headers) # headers as a dictionary
print(r.status_code) # status code
print(r.content) # body
r.close()
public class Main {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://google.com/example_data")
.build();
Response response = null;
try {
response = client.newCall(request).execute();
String responseAsStr = response.body().string();
response.code(); // code
response.headers(); // Headers
} catch (IOException e) {
e.printStackTrace();
}
}
}
class MyException(Exception):
def __init__(self, msg: str) -> None:
self.msg = msg
class MyError(ValueError):
def __init__(self, msg: str) -> None:
self.msg = msg
try:
# do something
raise MyException("Bad result")
# catch one of exceptions
except (ZeroDivisionError, RuntimeError, TypeError):
print('One of ZeroDivisionError, RuntimeError, TypeError throwed')
# custome Exception
except MyException as e:
print("MyException: ", e.msg)
# general, another exception
except:
print("any other error")
# continue in this block if there is no any exception catched
else:
print("All good, lets continue")
# finally, always active, no matter if any exception throwed or not
finally:
print("finally")
# MyException: Bad result
# finally
Example 1:
class MyException extends Exception{
MyException(String msg){
super(msg);
}
}
class MyError extends Error{
MyError(String msg){
super(msg);
}
}
public class Main {
public static void main(String[] args) {
try {
if(1==1)
throw new MyException("My test msg");
// catch concrete exception object
} catch (MyException e){
System.out.println("MyException throwed. Msg: " + e.getMessage());
// catch one of ArrayIndexOutOfBoundsException | NullPointerException exception
} catch (ArithmeticException | NullPointerException e){
System.out.println("One of ArrayIndexOutOfBoundsException | NullPointerException throwed");
// catch any other exception
} catch(Exception e) {
System.out.println("General exception");
// run this block always - no matter if any exception trowed or not
} finally{
System.out.println("finally");
}
// MyException throwed. Msg: My test msg
// finally
}
/**
* testMethod
*
* method with possible exception to be handled
*
* @throws MyException
*/
public void testMethod() throws MyException {
if(1==1)
throw new MyException("My test msg");
}
}
Example 2 (try - catch with resources)
public class Main {
public static void main(String[] args) {
String path = "/";
try(BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(path))) {
// ...
} catch (IOException e) {e.printStackTrance();}
}
}
Approach with Singledispatch.
see https://pypi.org/project/singledispatch/
Example:
import logging
from abc import ABC, abstractmethod
from datetime import datetime
from functools import singledispatch
from typing import Union, Dict
############# classes ##################
class Stringifable(ABC):
"""
The Stringifable interface
"""
@abstractmethod
def get_as_string(self) -> str:
pass
class ObjectStateLogger(ABC):
"""
The Loggable interface
"""
def __init__(self,plevel=logging.INFO,pname='general.log'):
logging.basicConfig(level=plevel)
self.__logger = logging.getLogger(pname)
def log_stringifable(self) -> None:
if isinstance(self, Stringifable):
self.__logger.info(self.get_as_string())
else:
raise Exception("Not Stringifable element")
class StaticLogger:
"""
Default, simple static logger
"""
_logger = None
@staticmethod
def log(msg: str, name: str = 'static_logger') -> None:
if StaticLogger._logger is None:
logging.basicConfig(plevel=logging.INFO)
StaticLogger._logger = logging.getLogger(name)
StaticLogger._logger.info(msg)
############# example of objects (entities) ##################
class EntityTag(ABC):
pass
class SimpleEntityTag(ABC):
pass
class Entity(ObjectStateLogger, Stringifable, EntityTag):
"""
Example of Entity object
"""
def __str__(self) -> str:
key_val: Dict[str,str] = {}
for i in vars(self):
istr: str = i.strip(' ').replace('_ObjectStateLogger','').replace('_Stringifable','')
if istr.startswith('__'):
continue
key_val[istr] = vars(self)[i]
return key_val
def __init__(self, name: str = None):
super().__init__()
self.name = name
def get_as_string(self) -> str:
return f'[ {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} ] [ {self.__class__} ] {self.__str__()}'
class SimpleEntity(Stringifable,SimpleEntityTag):
"""
Another example of entity
"""
def __init__(self, name: str = None):
super().__init__()
self.name = name
def get_as_string(self) -> str:
return f'[ {datetime.now().strftime("%Y-%m-%d %H:%M:%S")} ] [ {self.__class__} ] {self.name}'
############# Single Dispatch - register some loggers ##################
@singledispatch
def log_state(obj):
raise NotImplementedError("Cannot log state for this object")
"""
Register logger objects with included logger (extending ObjectStateLogger)
"""
@log_state.register(ObjectStateLogger)
def _(obj:Union[ObjectStateLogger,Stringifable]):
if not (isinstance(obj,ObjectStateLogger) and isinstance(obj,Stringifable)):
raise NotImplementedError("Object must be Union[ObjectStateLogger, Stringifable]")
try:
obj.log_stringifable()
except Exception as e:
print(e)
"""
Register another logger for siple objects without any other logger inside
"""
@log_state.register(Stringifable)
def _(obj: Stringifable):
try:
StaticLogger.log(obj.get_as_string())
except Exception as e:
print(e)
"""
Register one more logger for simple integers
"""
@log_state.register(int)
def _(*obj: int):
try:
StaticLogger.log(f'List of integers: {obj}')
except Exception as e:
print(e)
log_state(Entity("My entity"))
log_state(SimpleEntity("My simple entity"))
log_state(1, 2, 3, 4, 5)
# Results:
# INFO:general.log:[ 2019-07-14 20:47:56 ] [ <class '__main__.Entity'> ] {'name': 'My entity'}
# INFO:static_logger:[ 2019-07-14 20:47:56 ] [ <class '__main__.SimpleEntity'> ] My simple entity
# INFO:static_logger:List of integers: (1, 2, 3, 4, 5)
Example with custom logger/printer and handler passed to logger as a parameter.
###### test classes ######
class User:
"""
User class
"""
def __str__(self) -> Any:
return f'{self.__class__.__name__ } {{{self.name}}}'
def __init__(self, name):
self.name = name
class GreatUser(User):
"""
GreatUser class
"""
def __init__(self, name):
super().__init__(name)
class SuperUser(User):
"""
SuperUser class
"""
def __init__(self, name):
super().__init__(name)
###### static logger ######
class StaticLogger:
"""
Default, simple static logger
"""
_logger = None
@staticmethod
def log(obj: List[User], log_producer: Callable[[Type[User]], str] = lambda el: str(el)) -> None:
if StaticLogger._logger is None:
logging.basicConfig(level=logging.INFO)
StaticLogger._logger = logging.getLogger("static_logger")
pass
"""
call custom handler
"""
for i in obj:
log_producer(i)
######### Logger registration ########
@singledispatch
def log_objects(obj):
raise NotImplementedError("Cannot log state for this object")
"""
Register logger
"""
@log_objects.register(User)
def _(*obj:User):
try:
StaticLogger.log(obj, lambda uobj: print("User Logger: " + str(uobj)))
except Exception as e:
print(e)
log_objects(User("Normal user"), GreatUser("Great user"), SuperUser("Great user"))
# Result:
# User Logger: User {Normal user}
# User Logger: GreatUser {Great user}
# User Logger: SuperUser {Great user}
Example of generics in Java.
Generic class and methods.
import lombok.*;
import java.util.*;
import java.util.function.Function;
interface DomainObject{}
interface DtoAble {}
interface Dto{}
interface ComparableByExperience<T extends Employee>{
int compareByExperience(T obj2);
}
@Data
abstract class Person implements DomainObject {
protected String name;
protected int age;
protected int personUniqueId;
@Override
public String toString() {
return "[person]["+personUniqueId+"]";
}
}
@Getter @Setter @ToString(callSuper=true)
class Employee extends Person implements ComparableByExperience<Employee> {
private int yearsOfExperience = 0;
private String name;
public Employee(String name) {
this.name = name;
}
@Override
public int compareByExperience(Employee obj2) {
if(this.getYearsOfExperience() == obj2.getYearsOfExperience()){
return 0;
} else if(this.getYearsOfExperience() > obj2.getYearsOfExperience()) {
return 1;
} else {
return -1;
}
}
}
/**
* Contractor is not an Employee
*/
@Getter @Setter @ToString(callSuper=true)
class Contractor extends Person {
public Contractor() {}
}
/**
* Supplier is not an Employee
*/
@Getter @Setter @ToString(callSuper=true)
class Supplier extends Person {
public Supplier() {}
}
@Getter @Setter @ToString(callSuper=true)
class Manager extends Employee {
public Manager(String name) {
super(name);
}
}
@Getter @Setter @ToString(callSuper=true)
class UpperManager extends Manager {
public UpperManager(String name) {
super(name);
}
}
@Getter @Setter @ToString(callSuper=true)
class Director extends UpperManager {
public Director(String name) {
super(name);
}
}
class EmployesByExperience<T extends Employee>
{
private SortedSet<T> allEmployes = new TreeSet<T>(T::compareByExperience);
public EmployesByExperience(T ...employes) {
allEmployes.addAll(Arrays.asList(employes));
}
public T getTheMostExperienceEmployee(){
return this.allEmployes.first();
}
public T getTheLessExperiencedEmployee(){
return this.allEmployes.last();
}
}
class HROperations {
public static <S extends Employee, T extends Employee> List<T> updatePosition(List<S> employes, Function<S,T> update) {
List<T> trg = new ArrayList<>();
for(S e : employes) trg.add(update.apply(e));
return trg;
}
public static Map<? super Director, Boolean> informUpperManagement(
List<? super Director> people,
List<Class<? extends Person>> importantPositions
) {
Map<Person, Boolean> informedPeople = new HashMap<>();
people.stream().filter(
p -> importantPositions
.stream()
.anyMatch(pos -> pos.isAssignableFrom(p.getClass()))
).forEach(importantPerson -> {
// do some stuff... inform needed people
informedPeople.put((Person) importantPerson, true);
});
return informedPeople;
}
}
Test
import lombok.*;
import java.util.*;
import java.util.function.Function;
public class Main {
public static void main(String[] args) {
Director director = new Director("John Doe");
UpperManager upperManager = new UpperManager("John Alan");
UpperManager upperManager2 = new UpperManager("Marie Dane");
UpperManager upperManager3 = new UpperManager("Adrien Doe");
Manager manager = new Manager("Miroslav Doe");
Manager manager2 = new Manager("Aaron Doe");
Manager manager3 = new Manager("Nikita Doe");
Employee employee = new Employee("Ivan Bee");
Employee employee2 = new Employee("Mark Doe");
Employee employee3 = new Employee("Johny Lee");
// ### test generic method with wildcards
List<Employee> people = Arrays.asList(
director,
upperManager,
upperManager2,
upperManager3,
manager,
manager2,
manager3,
employee,
employee2,
employee3
);
List<Class<? extends Person>> upperManagement = new ArrayList<>();
upperManagement.add(Director.class);
upperManagement.add(UpperManager.class);
// inform all upper managers
Map<? super Director, Boolean> informedPeople = HROperations.informUpperManagement(people, upperManagement);
System.out.println(informedPeople);
// {UpperManager(super=Manager(super=Employee(super=[person][0], yearsOfExperience=0, name=John Alan)))=true,
// UpperManager(super=Manager(super=Employee(super=[person][0], yearsOfExperience=0, name=Adrien Doe)))=true,
// UpperManager(super=Manager(super=Employee(super=[person][0], yearsOfExperience=0, name=Marie Dane)))=true,
// Director(super=UpperManager(super=Manager(super=Employee(super=[person][0], yearsOfExperience=0, name=John Doe))))=true}
// ### test generic class
director.setYearsOfExperience(20);
upperManager.setYearsOfExperience(4);
manager.setYearsOfExperience(22);
employee.setYearsOfExperience(24);
EmployesByExperience<Employee> EmployesByExperience = new EmployesByExperience(
director,
upperManager,
manager,
employee
);
System.out.println(EmployesByExperience.getTheLessExperiencedEmployee());
System.out.println(EmployesByExperience.getTheMostExperienceEmployee());
/* result
Employee(super=[person][0], yearsOfExperience=24, name=Ivan Bee)
UpperManager(super=Manager(super=Employee(super=[person][0], yearsOfExperience=4, name=John Alan)))
*/
// ### test another generic method - transformer
// give a bonus to all managers or upper managers or director with more then 20 years of experience
HROperations.updatePosition(people, person -> {
if(person instanceof Manager) {
if(person.getYearsOfExperience() > 20){
System.out.println("Give a bonus to: " + person);
}
}
return person;
});
/*
result:
Give a bonus to: Manager(super=Employee(super=[person][0], yearsOfExperience=22, name=Miroslav Doe))
*/
}
}
class Vehicle:
pass
class Car(Vehicle):
def __repr__(self) -> str:
return f'Car({[self.__name, self.speed, self._shortname]!r})'
def __init__(self, name: str = "MyCar", speed: int = 100, shortname: str = "car short",
supername: str = "super") -> None:
# private field
self.__name = name
# public field
self.speed = speed
# convension from PEP 8 - protected field
self._shortname = shortname
# private field decorated by @property
self.__supername = supername
# test private method
self.__privateMethod()
# setter for private field
def set_name(self, name: str) -> None:
self.__name = name + "[setter 1]"
# getter for private field
def get_name(self) -> str:
return self.__name + "[getter 1]"
# getter by property decorator
@property
def supername(self) -> str:
return self.__supername + "[getter 2]"
# setter by property decorator
@supername.setter
def supername(self, supername: str) -> None:
self.__supername = supername + "[setter 2]"
print(self.__supername)
# deleter by decorator
@supername.deleter
def supername(self) -> None:
del self.__supername
# private method
def __privateMethod(self) -> None:
print("__privateMethod")
# public method
def publicMethod(self) -> None:
pass
# public class method (is bound to the class object itself)
@classmethod
def giveMeAnyFastCar(cls: 'Car', speed: int = 200) -> 'Car':
return cls("Fast Car", speed)
# static method (knows nothing about the class and just deals with the parameters)
@staticmethod
def getSpeedInKmPerHour(speedInMilesPerHour: int):
return speedInMilesPerHour * 1.60
Test
car: Car = Car()
# get name via getter
print("1. " + car.get_name())
# 1. MyCar[getter 1]
# set via setter
car.set_name("New car 2")
# try to get property normally (ERROR, property is private)
# print("2. " + car.name)
# try to get property "speed" normally (OK, property is public)
print(car.speed) # 100
# try to get property "_shortname" normally (OK, property is public/protected - bad practice but there is no any error)
print("3. " + car._shortname) # 3. car short
# try to get private property "supername" normally (OK but property is returned through getter supername()
print("4. " + car.supername) # 4. super[getter 2]
# try to set private property "supername" normally (OK but property is settled through setter supername()
car.supername = "another super name" # another super name[setter 2]
# try to run private method (ERROR)
# car.privateMethod()
# try to run public method (OK)
car.publicMethod()
car.publicMethod() # OK
# class method usage
fast_car: Car = Car.giveMeAnyFastCar(300)
print(fast_car) # Car(['Fast Car', 300, 'car short'])
# static method usage
speed: int = Car.getSpeedInKmPerHour(100)
print(speed) # 160.0
Descriptor example
class Email():
EMAIL_REGEX: Pattern[str] = re.compile(
r"[A-Za-z0-9-_]+(.[A-Za-z0-9-_]+)*@"
"[A-Za-z0-9-]+(.[A-Za-z0-9]+)*(.[A-Za-z]{2,})"
)
"""Descriptor class for an email address."""
def __init__(self) -> None:
self.pool = {}
def __get__(self, instance: Any, owner: Any) -> Any:
print("get Email through descriptor")
return self.pool.get(instance, 0)
def __set__(self, instance: Any, email_address: str) -> None:
if not Email.EMAIL_REGEX.match(email_address):
raise ValueError
print("set Email through descriptor")
self.pool[instance] = email_address
class User:
email: Email = Email()
def __init__(self, name: str, email: str) -> None:
self.email = email
self.name = name
# test get/set through descriptor
user: User = User("Sla", "[email protected]") # OK (set Email through descriptor)
print(user.email) # OK (get Email through descriptor), result: [email protected]
interface VehicleInterface {
void drive();
}
class Vehicle implements VehicleInterface {
/**
* interfaces' method implementation
*/
public void drive() {}
/**
* doSomethingInProtected - Protected method
*
* @return boolean
*/
protected boolean doSomethingInProtected(){
return true;
}
}
/**
* Class can be public, private or protected (inner class) or
* can don't have any modifier
*/
public class Car extends Vehicle {
/**
* public static property
*/
public static int staticProperty = 1;
/**
* constant
*/
public static final char CONSTANT = 'C';
/**
* getName - getter for provate property
*
* @return
*/
public String getName() {
return name;
}
/**
* setName - setter for provate property
*
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* private property name
* (access only in this class)
*/
private String name;
/**
* property without any modifier
* (access in the same package, in class and subclasses but only in the same package)
*/
String supername;
/**
* protected property shortname
* (access in the same package, in class and subclasses in any package)
*/
protected String shortname;
/**
* public property speed
* (access everywhere, even outside the package)
*/
public Integer speed = 100;
/**
* Constructor
*
* @param name
* @param supername
* @param shortname
*/
Car(String name, String supername, String shortname) {
this.name = name;
this.supername = supername;
this.shortname = shortname;
// call protected method in subclass
this.doSomethingInProtected();
// call private (possible only in this class)
this.doSomethingInPrivate();
// run static method (outside the object context)
Car.doSomethingStatically();
}
/**
* Another constructor with different signature
*
* @param name
* @param supername
*/
Car(String name, String supername) {
this(name, supername, "Default name");
}
/**
* doSomethingInPublic - Public method
*
* @return boolean
*/
public boolean doSomethingInPublic(){
return true;
}
/**
* doSomethingInPrivate - Private method
*
* @return boolean
*/
private boolean doSomethingInPrivate(){
return true;
}
/**
* doSomethingStatically - public static method
*
* @return
*/
public static boolean doSomethingStatically(){
return true;
}
}
Testing
public class Main {
public static void main(String[] args) {
System.out.println("test");
Car car = new Car("Truck","My great car");
// set private property through setter
car.setName("Carname");
// get private property through getter
System.out.println(car.getName());
// access to protected properties (access in the same package, in class and subclasses in any package)
car.shortname = "Shortname";
System.out.println(car.shortname); // Shortname
// access to public property (access everywhere, even outside the package)
System.out.println(car.speed ); // 100
// car.doSomethingInPrivate(); // this doesnt work (method is private)
car.doSomethingInProtected(); // this will work (the same package)
// call static method in class', not an object context
Car.doSomethingStatically();
// call public method in object's context
car.doSomethingInPublic();
// use static property
System.out.println(Car.staticProperty); // 1
// public constant
System.out.println(Car.CONSTANT); // C
}
}
Lombok style (@see https://projectlombok.org)
/**
* CarShort
*
* Lombok Style
* all properties are private and there are Getters and Setters for each one
* also constructor with required args (see @NonNull annotation)
*/
@Data
class CarShort extends Vehicle {
public static int staticProperty = 1;
public static final char CONSTANT = 'C';
@NonNull // if we want to have this property in "Required args constructor"
private String name;
private String supername;
private String shortname;
private Integer speed = 100;
}
class Outer:
"""Outer Class"""
def __init__(self):
# instantiating the 'Inner' class
self.inner = self.Inner()
def reveal(self):
# calling the 'Inner' class function inner_method
self.inner.inner_method("Call inner_method")
class Inner:
"""Inner Class"""
def inner_method(self, msg):
print(msg)
class InnerAnother:
"""Inner Class"""
def another_inner_method(self, msg):
print(msg)
# Access to inner class
Outer().Inner().inner_method("Calling inner_method from Inner class")
# Another example - instantiating InnerAnother class
outer = Outer()
inner = outer.InnerAnother()
inner.another_inner_method("Done!")
/**
* Outer and inner class
*/
class Outer {
/**
* inner class
*/
private class Inner {
public void print() {
System.out.println("Inner class");
}
}
/**
* Accessing class inner inside method
*/
void doSomething() {
Inner inner = new Inner();
inner.print();
}
void testLocalInnerClass()
{
/**
* method-local inner class
*/
class LoalInner {
public void print() {
System.out.println("Local inner class");
}
}
LoalInner inner = new LoalInner();
inner.print();
}
}
/**
* Example of interface as a base for Anonymous inner class
*/
interface MyInterface {
public abstract void doSomething();
}
/*public*/
class Test {
public static void main(String args[]) {
// Instantiating the outer class
Outer outer = new Outer();
// Accessing the doSomething() method.
outer.doSomething();
// test local inner class
outer.testLocalInnerClass();
/*
Example of anonymous inner class based on interface
*/
MyInterface obj = new MyInterface() {
@Override
public void doSomething() {
System.out.println("Anonybous inner class");
}
};
/*
The same example as above - Lambda expression approach
*/
MyInterface obj2 = () -> System.out.println("Anonybous inner class");
/*
annonymous inner class' object as a parameter of method
*/
testAnonymous(() -> System.out.println("Anonybous inner class"));
}
protected static void testAnonymous(MyInterface obj){
obj.doSomething();
}
}
Multiple inheritance example:
class MathA:
def add(self, a, b):
return a + b;
class MathB:
def multiply(self, a, b):
return a * b;
class MathC:
def power(self, a, b):
return a ** b;
class Calculation(MathA, MathB, MathC):
def divide(self, a, b):
return a / b;
d = Calculation()
print(d.add(10, 30))
print(d.multiply(10, 30))
print(d.divide(20, 50))
print(d.power(2, 4))
# 40
# 300
# 0.4
# 16
Resolving "Diamond problem":
class MathAD:
def calculate(self, a, b):
print("do sum")
return a + b
class MathBD:
def calculate(self, a, b):
print("do multiply")
return a * b
class MathCD:
def calculate(self, a, b):
print("do power")
return a ** b
class Calculation(MathAD, MathBD, MathCD):
def calculate(self, a, b):
result = MathAD.calculate(self, a, b), MathBD.calculate(self, a, b), MathCD.calculate(self, a, b)
print("job done")
return result
calc = Calculation()
res = calc.calculate(2, 3)
print(res)
# do sum
# do multiply
# do power
# job done
# (5, 6, 8)
Multiple inheritance and solving Diamond Problem based on Interface's default method.
import java.util.HashMap;
import java.util.Map;
interface MathAD{
default int sum(int a, int b){
System.out.println("do sum");
return a + b;
}
default int calculate(int a, int b){
System.out.println("do sum as a calculation");
return sum(a, b);
}
}
interface MathBD{
default int multiply(int a, int b){
System.out.println("do multiply");
return a * b;
}
default int calculate(int a, int b){
System.out.println("do multiply as a calculation");
return this.multiply(a, b);
}
}
interface MathCD{
default int power(int a, int b){
System.out.println("do power");
return (int) Math.pow(a, b);
}
default int calculate(int a, int b){
System.out.println("do power as a calculation");
return this.power(a, b);
}
}
/*public*/
class Calculation implements MathAD, MathBD, MathCD {
public Map<String, Integer> testCalculate(int a, int b) {
Map<String, Integer> res = new HashMap<>();
res.put(MathAD.class.getName(), MathAD.super.calculate(a, b));
res.put(MathBD.class.getName(), MathBD.super.calculate(a, b));
res.put(MathCD.class.getName(), MathCD.super.calculate(a, b));
System.out.println(res);
return res;
}
@Override
public int calculate(int a, int b) {
int res = MathBD.super.calculate(a, b);
System.out.println(res);
return res;
}
}
public class Main {
public static void main(String[] args) {
Calculation calc = new Calculation();
calc.calculate(2,3);
calc.testCalculate(3,4);
// do multiply as a calculation
// do multiply
// 6
// do sum as a calculation
// do sum
// do multiply as a calculation
// do multiply
// do power as a calculation
// do power
// {MathBD=12, MathAD=7, MathCD=81}
}
}
Approach with new method.
class Logger():
def __new__(cls, *args, **kwds):
it = cls.__dict__.get("__it__")
if it is not None:
return it
cls.__it__ = it = object.__new__(cls)
it.init(*args, **kwds)
return it
def init(self, *args, **kwds):
pass
def log(self, msg):
pass
logger = Logger()
logger.log("abc")
another_logger = Logger()
another_logger.log("abc")
print(id(logger))
print(id(another_logger))
# 23358832
# 23358832
Metaclass approach
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class Logger(metaclass=Singleton):
__metaclass__ = Singleton
def __init__(self, start_point=0):
self.start_point = start_point;
def log(txt):
print("log", txt, 0)
class DifferentLogger(metaclass=Singleton):
__metaclass__ = Singleton
a = Logger(1)
b = Logger(2)
c = DifferentLogger()
d = DifferentLogger()
print(a is b) # True
print((a is c) or (a is d)) # FALSE
print(c is d) # True
# True
# False
# True
Singleton via decorator approach
def singleton(cls):
instances = {}
def getinstance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return getinstance
@singleton
class Logger:
def log(self, txt):
print("log", txt, 0)
@singleton
class AnotherLogger:
def log(self, txt):
print("another log", txt, 0)
logger = Logger()
another_logger = Logger()
another_different_logger = AnotherLogger()
another_different_logger_two = AnotherLogger()
print(logger is another_logger)
logger.log("abc")
another_logger.log("def")
another_different_logger.log("a")
another_different_logger_two.log("b")
print(logger is another_different_logger)
print(another_different_logger is another_different_logger_two)
# True
# log abc 0
# log def 0
# another log a 0
# another log b 0
# False
# True
class Logger {
// private constructor to block typical instantiation via new Logger()
private Logger() {}
public static Logger getInstance() {
return SingletonHolder.INSTANCE;
}
// any method
public void log() {}
/*
private, static, final inner class - will be available in outer class at all time
this class will be loaded only if method Logger.getInstance will be called
and then SingletonHolder.INSTANCE will instantiate Logger object
*/
private static class SingletonHolder {
private static final Logger INSTANCE = new Logger();
}
}
public class Main {
public static void main(String[] args) {
/* usage example */
Logger.getInstance().log();
}
}
Polymorphism on class and method level example (implementation with ABC - Abstract Base Classes)
import abc
from typing import Type, Tuple
# Interface Repository
class Repository(abc.ABC):
@abc.abstractmethod
def save_data(self) -> None:
pass
# DB_Repository
class DB_Repository(Repository):
def __init__(self):
pass
def save_data(self, data: str) -> None:
print("Save data to DB_Repository", " ", data)
# File_Repository
class File_Repository(Repository):
def __init__(self):
pass
def save_data(self, data: str) -> None:
print("Save data to File_Repository", " ", data)
class Test():
@staticmethod
def save_data_to_repository(obj: Type[Repository], data: str) -> Repository:
obj.save_data(data)
@staticmethod
def test_polymorphism_on_class_methods(repos: Tuple[Repository], data: str) -> None:
for repo in repos:
repo.save_data(data)
db_repo:DB_Repository = DB_Repository();
file_repo:File_Repository = File_Repository();
Test.save_data_to_repository(db_repo, "Data example 1")
Test.save_data_to_repository(file_repo, "Data example 2")
# Save data to DB_Repository Data example 1
# Save data to File_Repository Data example 2
Test.test_polymorphism_on_class_methods((db_repo, file_repo), "test")
# Save data to DB_Repository test
# Save data to File_Repository test
Example of polymorphism.
import java.util.Arrays;
interface Repository{void save_data(String data);}
class DB_Repository implements Repository{
@Override
public void save_data(String data) {
System.out.println("Save data to DB_Repository " + data);
}
}
class File_Repository implements Repository{
@Override
public void save_data(String data) {
System.out.println("Save data to File_Repository " + data);
}
}
public class Main {
public static void main(String[] args) {
Repository dbRepo = new DB_Repository();
Repository fileRepo = new File_Repository();
dbRepo.save_data("data 1");
fileRepo.save_data("data 2");
testMethods(dbRepo, fileRepo);
// Save data to DB_Repository data 1
// Save data to File_Repository data 2
// Save data to DB_Repository test data
// Save data to File_Repository test data
}
public static void testMethods(Repository ...repos) {
Arrays
.stream(repos)
.forEach(repo -> repo.save_data("test data"));
}
}
Example 1 - function and method decoration
from typing import *
import time
import types
RT = Callable[..., Dict[str, Any]]
BT = Callable[..., int]
# PythonDecorators/decorator_function_with_arguments.py
def decorator_check_duration(precision: int = 5) -> Callable[[BT], RT]:
def wrap(f: BT) -> RT:
def wrapped_f(**range: int) -> Dict[str, Any]:
start: float = time.time()
res = f(**range)
end: float = time.time()
return {"result": res, "time": round(end - start, precision)}
return wrapped_f
return wrap
# sum numbers function
def sumNumbers(**rangenum: int) -> int:
return sum(x for x in range(rangenum.get('start'), rangenum.get('stop')) if x % 3 == 0)
# decorated sum numbers function (do the same but also calculates
# time taken and returns it as a dictionary with key "time")
@decorator_check_duration(2)
def sumNumbersWithTimeChecking(**rangenum: int) -> Any:
return sumNumbers(**rangenum)
def sumNumbersNormally(**rangenum: int) -> int:
return sumNumbers(**rangenum)
# normal sum
print(sumNumbersNormally(start=1, stop=10000000)) # 16666668333333
# the same function but decorated by decorator (duration checker)
print(sumNumbersWithTimeChecking(start=1, stop=10000000)) # {'result': 16666668333333, 'time': 1.39}
Example 2 - Logger decorator (logging function's input/output to the DB)
class decorator_logger():
def __init__(self, logger: Callable[[Dict[str, str]], None]) -> None:
"""
register logger
"""
if callable(logger):
self.logger = logger
else:
self.logger = None
def __call__(self, f) -> Callable[..., int]:
"""
If there are decorator arguments, __call__() is only called
once, as part of the decoration process! You can only give
it a single argument, which is the function object.
"""
def wrapped_f(**rangenum: int) -> int:
res = f(**rangenum)
if self.logger:
self.logger({"input": rangenum, "output": res})
return res
return wrapped_f
# create callable logger
myLogger: Callable[[Dict[str, str]], None] = lambda x: print("Log to database: ", x)
# decorate function by logger, so capture and log input and output
@decorator_logger(myLogger)
def calculateSumB(**rangenum: int):
return sum(x for x in range(rangenum.get('start'), rangenum.get('stop')) if x % 3 == 0)
print(calculateSumB(start=1, stop=10000000))
# returned result: 16666668333333
# additionally method uses callable logger so below line is printed as well
# Log to database: {'input': {'start': 1, 'stop': 10000000}, 'output': 16666668333333}
Example 3 - Monkey Patching
class Class():
def power(self, x: int, y: int = 2) -> int:
return x ** y
def set_power_mode_x_by_x(self) -> None:
opower = self.power
def power_x_by_x(self, x: int) -> int:
return opower(x, x)
self.power = types.MethodType(power_x_by_x, self)
cls = Class()
print(cls.power(10)) # 100
cls.set_power_mode_x_by_x()
print(cls.power(10)) # 10000000000
AOP/AspectJ approach. Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect.
Simple Logger by decorator example:
package jlogger;
import lombok.extern.java.Log;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.Predicate;
/* --- interfaces --- */
interface LoggableRequest {String toString();}
interface LoggableResponse {String toString();}
interface LoggerHandler {
void init();
void end();
<S extends LoggableRequest> void logRequest(S request);
<T extends LoggableResponse> void logResponse(T response);
}
/**
* Main Logger interface - kind of Decorator
*/
@Retention(
RetentionPolicy.RUNTIME
)
@Target({
ElementType.CONSTRUCTOR,
ElementType.METHOD
})
//public
@interface Logger {
enum TYPE{
ALL,
REQUEST,
RESPONSE
}
TYPE[] logTypes() default {TYPE.ALL};
Class<? extends LoggerHandler> loggerHandler() default DefaultLogger.class;
}
@Log
class DefaultLogger implements LoggerHandler {
private static String JOIN_SEPARATOR = " | ";
private StringBuilder logResult = new StringBuilder();
@Override
public void init() {
}
@Override
public void end() {
log.info(StringUtils.strip(logResult.toString(), JOIN_SEPARATOR));
}
@Override
public void logRequest(LoggableRequest request) {
logResult.append(" request: ").append(request).append(JOIN_SEPARATOR);
}
@Override
public void logResponse(LoggableResponse response) {
logResult.append(" response: ").append(response).append(" | ");
}
}
/**
* Aspect J, class with pointcuts
*/
@Log
@Aspect
class AspectLogger {
@Pointcut(
"execution(* *(..)) && @annotation(Logger)"
)
public void loggerLog() {}
@Around("loggerLog()")
public Object around(ProceedingJoinPoint point) {
LoggerHandler loggerHandlerInstance = null;
Method method = ((MethodSignature) point.getSignature()).getMethod();
Logger logParams = method.getAnnotation(Logger.class);
Class<? extends LoggerHandler> loggerHandler = logParams.loggerHandler();
try {
loggerHandlerInstance = loggerHandler.getDeclaredConstructor().newInstance();
} catch (
InstantiationException
| IllegalAccessException
| InvocationTargetException
| NoSuchMethodException e
) {
log.warning(e.toString());
}
/*
----- parse RESPONSE -----
*/
Object jpObj = null;
/* LoggableResponse */
LoggableResponse response = null;
try {
jpObj = point.proceed();
if(jpObj instanceof LoggableResponse){
response = (LoggableResponse) jpObj;
}
Class responseEntityClazz = null;
if(responseEntityClazz.isInstance(jpObj)) {
Method getBodyMethod = responseEntityClazz.getMethod("getBody");
Object reqBody = getBodyMethod.invoke(jpObj);
if(reqBody instanceof LoggableResponse) {
response = (LoggableResponse) reqBody;
}
}
} catch (Throwable throwable) {
log.warning(throwable.toString());
}
/*
----- parse REQUEST -----
*/
LoggableRequest request = null;
try {
/* LoggableRequest */
request = Arrays
.stream(
point.getArgs()
)
.filter(
obj -> obj instanceof LoggableRequest
)
.map(
obj -> (LoggableRequest) obj
)
.findFirst()
.orElse(null);
} catch (Throwable throwable) {
log.warning(throwable.toString());
}
// log only when Rqeuset containg value ...
String requiredRequestField;
String requiredRequestValue;
// get log types, log only request, response, both of them, etc.
Logger.TYPE[] logTypes = logParams.logTypes();
Predicate<Logger.TYPE> logAll = el -> el.equals(Logger.TYPE.ALL);
Predicate<Logger.TYPE> logRequest = el -> el.equals(Logger.TYPE.REQUEST);
Predicate<Logger.TYPE> logResponse = el -> el.equals(Logger.TYPE.RESPONSE);
if(loggerHandlerInstance == null) {
return jpObj;
} else{
loggerHandlerInstance.init();
}
// log request
if(Arrays.stream(logTypes).anyMatch(logAll.or(logRequest)) && request != null){
loggerHandlerInstance.logRequest(request);
}
// log response
if(Arrays.stream(logTypes).anyMatch(logAll.or(logResponse)) && response != null){
loggerHandlerInstance.logResponse(response);
}
loggerHandlerInstance.end();
return jpObj;
}
}
/**
* Test
*/
class MyController{
/**
* example with parameters
*/
@Logger(logTypes = {Logger.TYPE.REQUEST, Logger.TYPE.RESPONSE}, loggerHandler=DefaultLogger.class)
public void method(){}
/**
* simple decoration, without any paramters
*/
@Logger
public void anotherMethod(){}
}
See more: https://bitbucket.org/slawekhaa/jlogger-java-aspectj/src
Spring Framework example - approach with Beans and Qualifier - the same method in 2 different versions (normal and extended), depending on autowired Bean. Extended version log elapsed time of our calulation to the database and returns the same result (uses sumNumbers() method from basic version of MathBasic - calculation class)
interface Calculations{
int sumNumbers(Range<Integer> range);
}
/**
* Basic calc class
*/
class MathBasic implements Calculations {
/**
* sumNumbers
* @param range
* @return int
*/
public int sumNumbers(Range<Integer> range){
return IntStream.range(
range.lowerEndpoint(),
range.upperEndpoint()
).sum();
}
}
/**
* class with elapsed time
* check on every method
*/
class MathWithTimer implements Calculations {
/**
* sumNumbers with logging elapsed time to the DB
*
* @param range
* @return Map<String, Number>
*/
public int sumNumbers(Range<Integer> range){
long lStartTime = System.currentTimeMillis();
MathBasic mb = new MathBasic();
int result = mb.sumNumbers(range);
long elapsedTime = System.currentTimeMillis() - lStartTime;
// log elapsed time [ elapsedTime ] to DB ...
return result;
}
}
@Configuration
class MainConfig {
@Primary
@Bean(name = "MathBasic")
public Calculations calculations() {
return new MathBasic();
}
@Bean(name = "MathWithTimer")
public Calculations calculationsWithTimer() {
return new MathWithTimer();
}
}
@RestController
class TestController extends BaseController {
/**
* normal calculator
* standard method sumNumbers()
*/
@Autowired
private Calculations calculator;
/**
* extended calculator with timer - basic calculator decorated
* by elapsed time logger
*/
@Autowired
@Qualifier( "MathWithTimer")
private Calculations calculatorExtended;
@RequestMapping(value = "/sum", method = RequestMethod.GET)
public void getSum(){
int res = calculator.sumNumbers(Range.open(1,100000));
}
@RequestMapping(value = "/sum-extended", method = RequestMethod.GET)
public void getSumExtended(){
int res = calculatorExtended.sumNumbers(Range.open(1,100000));
}
}
Java EE example - approach with Decorator annotation - method sumNumbers() may behave normally and just calculate sum of integers frm range or, if we register decorator - we may use extended version of that method with elapsed time logging. Decorated method uses the same logic and returns the same result but log in elapsed time to the DB.
package mypackage;
import com.google.common.collect.Range;
import java.util.stream.IntStream;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.decorator.Decorator;
import javax.decorator.Delegate;
import javax.enterprise.inject.Any;
interface Calculations{
int sumNumbers(Range<Integer> range);
}
@Decorator
abstract class CalculatorDecorator implements Calculations {
@Inject
@Delegate
@Any
private Calculations calculator;
public int sumNumbers(Range<Integer> range) {
long lStartTime = System.currentTimeMillis();
int res = calculator.sumNumbers(range);
long elapsedTime = System.currentTimeMillis() - lStartTime;
// log elapsed time [ elapsedTime ] to DB ...
return res;
}
}
class BasicCalculator implements Calculations {
@Override
public int sumNumbers(Range<Integer> range) {
int sumOfRange = IntStream.range(
range.lowerEndpoint(),
range.upperEndpoint()
).sum();
return sumOfRange;
}
}
@Path("/")
class TestController {
@Inject
private Calculations calculator;
@GET
@Path("/")
public void testSum() {
int res = calculator.sumNumbers(Range.open(1,100000));
}
}
Add decorator in beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<decorators>
<class>mypackage.CalculatorDecorator</class>
</decorators>
</beans>
Another example of Decorator Design Pattern in Java - see https://bitbucket.org/slawekhaa/design-patterns-java-examples/wiki/Home
Custom annotations is very powerful mechanism in Java. We may use it to create decorators by many ways e.g. we can track and modify or log method's I/O or extend methods and classes functionality and do many various things with it.
Example of custom annotations - registering buttons Event handlers.
import javax.swing.*;
import java.awt.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// --------------------- custom annotation - decorator ---------------------
/**
* Custom annotation - action listener decorator
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
/*public*/ @interface RegisterActionListener
{
String value() default "";
String[] values() default {};
Class<? extends CustomHandler> handlerClass() default DefaultHandler.class;
}
// --------------------- custom handler - example of decorator's parameter ---------------------
/**
* Custom habdler interface
*/
interface CustomHandler{
void handle(Object element);
}
class DefaultHandler implements CustomHandler{
@Override
public void handle(Object element) {}
}
/**
* my custom handler example
*/
class MyHandler implements CustomHandler{
@Override
public void handle(Object element) {
System.out.println("handle");
}
}
// --------------------- action processor, based on reflection ---------------------
/*public*/ class ActionListenerInstaller
{
/**
* processAnnotations - annotation processor
* @param obj
*/
public static void processAnnotations(Object obj)
{
try
{
Class<?> cl = obj.getClass();
for (Method m : cl.getDeclaredMethods())
{
RegisterActionListener a = m.getAnnotation(RegisterActionListener.class);
Class<? extends CustomHandler> handler = a.handlerClass();
if (a != null)
{
if(a.values().length > 0) {
for (String v : a.values()) {
Field f = setFieldAccessible(cl, v);
addListener(f.get(obj), obj, m, handler);
}
}
if(!a.value().isEmpty()) {
for (String v : a.values()) {
Field f = setFieldAccessible(cl, v);
addListener(f.get(obj), obj, m, handler);
}
}
}
}
} catch (Exception e) {}
}
private static Field setFieldAccessible(Class<?> cl, String fieldName) throws NoSuchFieldException {
Field f = cl.getDeclaredField(fieldName);
f.setAccessible(true);
return f;
}
/**
* Add listener to source element
*
* @param source
* @param param
* @param m
* @throws Exception
*/
public static void addListener(Object source, final Object param, final Method m, Class<? extends CustomHandler> customHandler)
throws ReflectiveOperationException
{
InvocationHandler handler = (proxy, mm, args) -> m.invoke(param);
Object listener = Proxy.newProxyInstance(null, new Class[] { java.awt.event.ActionListener.class }, handler);
Method adder = source.getClass().getMethod("addActionListener", java.awt.event.ActionListener.class);
adder.invoke(source, listener);
// use custom handler to handle action if any
if(customHandler != DefaultHandler.class) {
Method method = customHandler.getMethod("handle", String.class);
Object o = method.invoke(source);
}
}
}
// --------------------- decorator's/annotation's @RegisterActionListener usage ---------------------
/**
* Example of usage - class with 2 buttons and
* 2 action handlers registered by annotation/decorator
*/
/*public*/ class MyButtonsFrame extends JFrame
{
private JPanel panel;
private JButton buttonA;
private JButton buttonB;
public MyButtonsFrame()
{
panel = new JPanel();
add(panel);
buttonA = new JButton("My button A");
buttonB = new JButton("My button B");
panel.add(buttonA);
panel.add(buttonB);
ActionListenerInstaller.processAnnotations(this);
}
/**
* register this action to be called on buttonA click
*/
@RegisterActionListener("buttonA")
public void yellowBackground()
{
panel.setBackground(Color.YELLOW);
}
/**
* register action on 2 buttons
*/
@RegisterActionListener(values = {"buttonB", "buttonA"})
public void blueBackground()
{
panel.setBackground(Color.BLUE);
}
/**
* register action with custom handler class
*/
@RegisterActionListener(values = {"buttonB", "buttonA"}, handlerClass = MyHandler.class)
public void redBackground()
{
panel.setBackground(Color.RED);
}
}
from abc import ABC, abstractmethod
from typing import Any
class Builder(ABC):
"""
The Builder interface
"""
@abstractmethod
def set_name(self, name: str) -> None:
pass
@abstractmethod
def set_price(self, price: float) -> None:
pass
@abstractmethod
def set_amount(self, amount: int) -> None:
pass
@abstractmethod
def add_ingredient(self, ingredient: str) -> None:
pass
@abstractmethod
def calculate_calories(self) -> int:
pass
@abstractmethod
def pack_product(self) -> None:
pass
@abstractmethod
def build(self) -> None:
pass
class Product():
def __init__(self) -> None:
"""
non arguments constructor
"""
self.__name = ""
self.__price = None
self.__amount = None
self.__callories = None
self.parts = [] # ingredients
def __str__(self) -> None:
return f"Produc: {self.name}, price:{self.price}, ingredients: {', '.join(self.parts)}"
@property
def name(self) -> str:
return self.__name
@name.setter
def name(self, name: str) -> None:
self.__name = name
@property
def price(self) -> float:
return self.__price
@price.setter
def price(self, price: float) -> None:
self.__price = price
@property
def amount(self) -> int:
return self.__amount
@amount.setter
def amount(self, amount: int) -> None:
self.__amount = amount
def add_ingredient(self, part: str) -> None:
self.parts.append(part)
def calculate_calories(self) -> int:
print("calculate callories")
for i in self.parts:
self.__callories += 10
return self.__callories
def pack_product(self) -> None:
print("pack product")
pass
class ProductBuilder(Builder):
"""
The Concrete Builder classes follow the Builder interface and provide
specific implementations of the building steps. Your program may have
several variations of Builders, implemented differently.
"""
def __init__(self) -> None:
"""
A new builder instance should contain a blank product object
"""
self.reset()
def reset(self) -> None:
"""
Reset builder, create a new empty opject inside
"""
self.__product = Product()
def __product(self) -> Product:
"""
private product method, to disallow getting Product
and force us to use build() method instead
"""
pass
def build(self) -> Product:
"""
Build a Product object and return.
After build reset Builder to be prepared for further
usage and for any new builds
"""
product = self.__product
self.reset()
return product
def set_name(self, name: str) -> None:
self.__product.name = name
def set_price(self, price: float) -> None:
self.__product.price = price
def set_amount(self, amount: int) -> None:
self.__product.amount = amount
def add_ingredient(self, ingredient: str) -> None:
self.__product.add_ingredient(ingredient)
def calculate_calories(self) -> int:
return self.__product.calculate_calories()
def pack_product(self) -> None:
self.__product.pack_product()
class Director:
"""
The Director is used if we want to order building steps in a
particular sequence.
"""
def __init__(self) -> None:
self._builder = None
@property
def builder(self) -> Builder:
return self._builder
@builder.setter
def builder(self, builder: Builder) -> None:
"""
The Director works with any builder instance that the client code passes
to it. This way, the client code may alter the final type of the newly
assembled product.
"""
self._builder = builder
"""
We can set up many various sequences of building process
"""
def build_product_with_calories_info(self, name: str, amount: int) -> Any:
self.builder.set_name(name)
self.builder.set_amount(amount)
self.builder.calculate_calories()
return self.builder.build()
def build_product_and_pack(self, name: str, amount: int) -> Any:
self.builder.set_name(name)
self.builder.set_amount(amount)
self.builder.pack_product()
return self.builder.build()
if __name__ == "__main__":
builder = ProductBuilder()
# #### build products
builder.set_name("Product 1")
builder.set_price(122.12)
builder.set_amount(30)
builder.add_ingredient("Ingredient A")
builder.add_ingredient("Ingredient B")
product1: Product = builder.build();
builder.set_name("Product 2")
builder.set_price(31.12)
builder.set_amount(10)
product2: Product = builder.build();
print(product1, product2)
print(product1 is product2)
# Produc: Product 1, price:122.12, ingredients: Ingredient A, Ingredient B Produc: Product 2, price:31.12, ingredients:
# False
# #### using Director for setting up building steps in an order
director = Director()
my_builder = ProductBuilder()
director.builder = my_builder
product3: Product = director.build_product_with_calories_info("Procut 3", 1)
product4: Product = director.build_product_and_pack("Procut 4", 1)
print(product3, product4)
# calculate
# callories
# pack product
# Produc: Procut 3, price: None, ingredients: Produc: Procut 4, price: None, ingredients:
import lombok.Data;
import java.math.BigDecimal;
import java.math.RoundingMode;
@Data
class Product {
private String name;
private BigDecimal price;
private Integer amount;
private Product(String name, BigDecimal price, Integer amount) {
this.name = name;
this.price = price;
this.amount = amount;
}
/**
* Builder as an inner class inside our outer class
*/
public static class ProductBuilder {
// some default values (recommended)
private String name;
private BigDecimal price;
private Integer amount = 1;
public ProductBuilder setName(String name) {
this.name = name;
return this;
}
public ProductBuilder setPrice(BigDecimal price) {
this.price = price.setScale(2, RoundingMode.HALF_DOWN);
return this;
}
public ProductBuilder setAmount(Integer amount) {
this.amount = amount;
return this;
}
public Product build() {
// Inner class ProductBuilder has an access to private constructor
// from outer class Product, so we can instantiate Product object here
return new Product(name, price, amount);
}
}
}
public class Main {
public static void main(String[] args) {
Product Product = new Product.ProductBuilder()
.setName("Car")
.setAmount(3)
.setPrice(new BigDecimal(10.15))
.build();
System.out.println(Product);
// Product(name=Car, price=10.15, amount=3)
}
}
- Slawomir Hadas - author - Github