Guava: Functional Programming with Guava
14987 단어 guava
2. Predicate
3. Supplier
1. Function & Functions
1> Function
With Java in its current state, we don't have closures as they exist in other languages.
For now, Java's answer to closures is to use anonymous classes.
While an anonymous class functions effectively in the same way as a closure, the syntax can be bulky and when used too much, can make your code hard to follow and maintain.
@Test
public void functionTest2() {
String dateStr = new Function<Date, String>() {
private final SimpleDateFormat dateFormat = new SimpleDateFormat(
"dd/MM/YYYY");
public String apply(Date input) {
return dateFormat.format(input);
}
}.apply(Calendar.getInstance().getTime());
logger.info(dateStr);
}
public String formatDate(Date date) {
return new SimpleDateFormat("dd/MM/YYYY").format(date);
}
Now compare the previous example of the anonymous class implementing Function interface.
This final example is much easier to read.
However, if you have a collection of Date objects and need to obtain a list contains the string representations of those date, the Function interface could be a better approach.
2> Functions.forMap
public static <K, V> Function<K, V> forMap(Map<K, V> map);
public static <K, V> Function<K, V> forMap(Map<K, ? extends V> map, @Nullable V defaultValue);
@Test
public void functionsTest() {
Map<String, State> map = Maps.newHashMap();
Function<String, State> stateLookUpFunction = Functions.forMap(map,
new State("UN", "Unknown"));
map.put("NY", new State("NY", "New York"));
map.put("CA", new State("CA", "California"));
map.put("LA", new State("LA", "Los Angeles"));
State state = stateLookUpFunction.apply("NY");
logger.info(state);
state = stateLookUpFunction.apply("MS");
logger.info(state);
}
There is one caveat to using the Functions.forMap method.
The map returned by Functions.forMap will throw an IllegalArgumentException if the given key is not found in the map.
Functions.forMap(map); // throw IllegalArgumentException if cannot find key
Functions.forMap(map, null); // return "null" if cannot find key
Functions.forMap(map, defaultValue); // return "defaultValue" if cannot find key
By using a Function interface to perform the state lookups, you can easily change out the implementation.
When combining it with a Splitter object to create a map or when using some of the other methods for map creation in Guava collection package,
we are leveraging the power of Guava in our code.
3> Functions.compose
public static <A, B, C> Function<A, C> compose(Function<B, C> g, Function<A, ? extends B> f) {
return new FunctionComposition<A, B, C>(g, f);
}
public FunctionComposition(Function<B, C> g, Function<A, ? extends B> f) {
this.g = checkNotNull(g);
this.f = checkNotNull(f);
}
@Override
public C apply(@Nullable A a) {
return g.apply(f.apply(a));
}
Return the composition of two functions. For f: A->B and g: B->C, composition is defined as the function h such that h(a) == g(f(a)) for each a.
@Test
public void composeTest() {
Map<String, State> map = Maps.newHashMap();
map.put("NY", new State("NY", "New York"));
map.put("CA", new State("CA", "California"));
map.put("LA", new State("LA", "Los Angeles"));
Function<String, String> function = Functions.compose(
new Function<State, String>() {
public String apply(State input) {
return input.getStateAddress();
}
}, Functions.forMap(map, new State("UN", "Unknown")));
logger.info(function.apply("NY")); // "New York"
logger.info(function.apply("DA")); // "Unknown"
// Functions.forMap() returns Function<String, State>
// Anonymous innner Function<State, String>
// The composed Function<String, String>
}
2. Predicate & Predicates
1> Predicate
The Predicate interface is a functional cousin to the Function interface.
public interface Predicate<T>{
boolean apply(T input)
}
@Test
public void applyTest() {
String input = "Davy Jones";
boolean containsKeyWords = new Predicate<String>() {
public boolean apply(String input) {
return input.contains("Davy");
}
}.apply(input);
logger.info(containsKeyWords);
}
2> Predicates.and
@Test
public void andTest() {
String str = "Davy Jones";
boolean containsKeyWords = Predicates.and(new Predicate<String>() {
public boolean apply(String input) {
return input.contains("Davy");
}
}, new Predicate<String>() {
public boolean apply(String input) {
return input.contains("Cool");
}
}).apply(str);
logger.info(containsKeyWords); //false
}
3> Predicates.or
@Test
public void orTest() {
String str = "Davy Jones";
boolean containsKeyWords = Predicates.or(new Predicate<String>() {
public boolean apply(String input) {
return input.contains("Davy");
}
}, new Predicate<String>() {
public boolean apply(String input) {
return input.contains("Cool");
}
}).apply(str);
logger.info(containsKeyWords); //true
}
4> Predicates.not
@Test
public void notTest() {
String str = "Davy Jones";
boolean containsKeyWords = Predicates.not(new Predicate<String>() {
public boolean apply(String input) {
return input.contains("Davy");
}
}).apply(str);
logger.info(containsKeyWords); //false
}
5> Predicates.compose
private CompositionPredicate(Predicate<B> p, Function<A, ? extends B> f) {
this.p = checkNotNull(p);
this.f = checkNotNull(f);
}
@Override
public boolean apply(@Nullable A a) {
return p.apply(f.apply(a));
}
@Test
public void composeTest() {
boolean isPersonMale = Predicates.compose(new Predicate<String>() {
public boolean apply(String input) {
return "Male".equals(input);
}
}, new Function<Person, String>() {
public String apply(Person input) {
return input.getGender();
}
}).apply(new Person("Davy", "Male"));
assertTrue(isPersonMale);
}
class Person {
String name;
String gender;
public Person(String name, String gender) {
super();
this.name = name;
this.gender = gender;
}
public String getName() {
return name;
}
public String getGender() {
return gender;
}
}
3. Supplier & Suppliers
1> Supplier
public interface Supplier<T>{
T get();
}
The Supplier interface helps us implement several of the typical creational patterns.
When get is called, we would always return the same instance(singleton) or a new instance with each invocation.
@Test
public void getTest() {
String str = new Supplier<String>() {
public String get() {
return "AAA";
}
}.get();
assertEquals("AAA", str);
}
@Test
public void getTest2() {
Predicate<String> predicate = new Supplier<Predicate<String>>() {
public Predicate<String> get() {
return new Predicate<String>() {
public boolean apply(String input) {
return "AAA".equals(input);
}
};
}
}.get();
assertTrue(predicate.apply("AAA"));
assertFalse(predicate.apply("BBB"));
}
2> Suppliers.memoize public static <T> Supplier<T> memoize(Supplier<T> delegate) {
return (delegate instanceof MemoizingSupplier)
? delegate
: new MemoizingSupplier<T>(Preconditions.checkNotNull(delegate));
}
MemoizingSupplier(Supplier<T> delegate) {
this.delegate = delegate;
}
@Override public T get() {
// A 2-field variant of Double Checked Locking.
if (!initialized) {
synchronized (this) {
if (!initialized) {
T t = delegate.get();
value = t;
initialized = true;
return t;
}
}
}
return value;
}
We can see from the source code that the MemoizingSupplier is just a proxy for innerSupplier, it stores an inner instance which is returned by delegate. Everytime we call get(), the proxy will return this instance.
@Test
public void getTest() {
Supplier<Object> supplier = new Supplier<Object>() {
public Object get() {
return new Object();
}
};
Object obj1 = supplier.get();
Object obj2 = supplier.get();
assertNotEquals(obj1, obj2);
Supplier<Object> proxy = Suppliers.memoize(supplier);
obj1 = proxy.get();
obj2 = proxy.get();
assertEquals(obj1, obj2);
}
3> Suppliers.memoizeWithExpiration
public static <T> Supplier<T> memoizeWithExpiration(
Supplier<T> delegate, long duration, TimeUnit unit) {
return new ExpiringMemoizingSupplier<T>(delegate, duration, unit);
}
ExpiringMemoizingSupplier(
Supplier<T> delegate, long duration, TimeUnit unit) {
this.delegate = Preconditions.checkNotNull(delegate);
this.durationNanos = unit.toNanos(duration);
Preconditions.checkArgument(duration > 0);
}
@Override public T get() {
long nanos = expirationNanos;
long now = Platform.systemNanoTime();
if (nanos == 0 || now - nanos >= 0) {
synchronized (this) {
if (nanos == expirationNanos) { // recheck for lost race
T t = delegate.get();
value = t;
nanos = now + durationNanos;
expirationNanos = (nanos == 0) ? 1 : nanos;
return t;
}
}
}
return value;
}
@Test
public void memoizeWithExpirationTest() throws InterruptedException {
Supplier<Object> supplier = new Supplier<Object>() {
public Object get() {
return new Object();
}
};
Object obj1 = supplier.get();
Object obj2 = supplier.get();
assertNotEquals(obj1, obj2);
Supplier<Object> proxy = Suppliers.memoizeWithExpiration(supplier, 1L,
TimeUnit.SECONDS);
obj1 = proxy.get();
obj2 = proxy.get();
assertEquals(obj1, obj2);
obj1 = proxy.get();
Thread.sleep(1000 * 2);
obj2 = proxy.get();
assertNotEquals(obj1, obj2);
}
4> Suppliers.compose
public static <F, T> Supplier<T> compose(
Function<? super F, T> function, Supplier<F> supplier) {
return new SupplierComposition<F, T>(function, supplier);
}
SupplierComposition(Function<? super F, T> function, Supplier<F> supplier) {
this.function = function;
this.supplier = supplier;
}
@Override public T get() {
return function.apply(supplier.get());
}
@Test
public void composeTest() {
Map<String, String> map = Maps.newHashMap();
map.put("A", "A for alcohol");
map.put("B", "B for boycott");
Supplier<String> supplier = Suppliers.compose(Functions.forMap(map),
new Supplier<String>() {
public String get() {
return "A";
}
});
String str = supplier.get();
assertEquals("A for alcohol", str);
}
Summary:
We've seen how Guava can add some functional aspects to Java with the Function and Predicate interfaces.
The Function interface provides us with the ability to transform objects and the Predicate interface gives us a powerful machanism for filtering.
The Functions and Predicates classes also help us write code that is easier to maintain and much easier to change.
The Suppliers help by providing essential collaborating objects while completely hiding the details of how those objects are created.
Combined with a dependency injection framework such as a Spring or Guice, these interfaces will allow us to seamlessly change the behavior of our programs by simply providing a different implementation.
Reference Links:
1) "Getting Started with Google Guava"-Bill Bejeck
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Guava 학습노트-Function, Predicate먼저 실례에 필요한 실체 클래스를 도입하고 실체 클래스의 get/set 방법은 생략합니다 하나의 Function 인터페이스 구현: Function TestCase: Predicate Predicate 인터페이스 구현...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.