Let's assume I have a class Person
public class Person {
private final String name;
private final int age;
private boolean rejected;
private String rejectionComment;
public void reject(String comment) {
this.rejected = true;
this.rejectionComment = comment;
}
// constructor & getters are ommited
}
and my app is something like that
class App {
public static void main(String[] args) {
List<Person> persons = Arrays.asList(
new Person("John", 10),
new Person("Sarah", 20),
new Person("Daniel", 30)
)
persons.forEach(p -> {
rejectIfYoungerThan15(p);
rejectIfNameStartsWithD(p);
// other rejection functions
}
}
private static void rejectIfYoungerThan15(Person p) {
if (!p.isRejected() && p.getAge() < 15) {
p.reject("Too young")
}
}
private static void rejectIfNameStartsWithD(Person p) {
if (!p.isRejected() && p.getName().startsWith("D")) {
p.reject("Name starts with 'D'")
}
}
// other rejection functions
}
The thing is I don't like that I have to perform !p.isRejected() check in every rejection function. Moreover, it doesn't make sense to pass an already rejected person to next filters.
So my idea is to use a mechanism of Stream.filter and make something like
persons.stream().filter(this::rejectIfYoungerThan15).filter(this::rejectIfNameStartsWithD)...
And change signature for these methods to return true if a passed Person has not been rejected and false otherwise.
But it seems to me that it's a very bad idea to use filter with non-pure functions.
Do you have any ideas of how to make it in more elegant way?
When you change the check functions to only check the condition (i.e. not to call p.isRejected()) and return boolean, you already made the necessary steps to short-circuit:
private static boolean rejectIfYoungerThan15(Person p) {
if(p.getAge() < 15) {
p.reject("Too young");
return true;
}
return false;
}
private static boolean rejectIfNameStartsWithD(Person p) {
if(p.getName().startsWith("D")) {
p.reject("Name starts with 'D'");
return true;
}
return false;
}
usable as
persons.forEach(p -> {
if(rejectIfYoungerThan15(p)) return;
if(rejectIfNameStartsWithD(p)) return;
// other rejection functions
}
}
A Stream’s filter operation wouldn’t do anything other than checking the returned boolean value and bail out. But depending on the Stream’s actual terminal operation the short-circuiting could go even farther and end up in not checking all elements, so you should not bring in a Stream operation here.
Calling these methods from lambda is fine, however, for better readability, you can rename these methods to show what they are doing and return boolean, e.g.:
private boolean hasEligibleAge(Person p){..}
private boolean hasValidName(Person p){..}
Another approach would be to wrap these methods into another method (to reflect the business logic/flow), e.g.:
private boolean isEligible(Person p){
//check age
//check name
}
You should make Person immutable, and let the reject-methods return a new Person. That will allow you to chain map-calls. Something like this:
public class Person {
private final String name;
private final int age;
private final boolean rejected;
private final String rejectionComment;
public Person reject(String comment) {
return new Person(name, age, true, comment);
}
// ...
}
class App {
// ...
private static Person rejectIfYoungerThan15(Person p) {
if (!p.isRejected() && p.getAge() < 15) {
return p.reject("Too young");
}
return p;
}
}
Now you can do this:
persons.stream()
.map(App::rejectIfYoungerThan15)
.map(App::rejectIfNameStartsWithD)
.collect(Collectors.toList());
If you want to remove rejected persons, you can add a filter after the mapping:
.filter(person -> !person.isRejected())
EDIT:
If you need to short circuit the rejections, you could compose your rejection functions into a new function and make it stop after the first rejection. Something like this:
/* Remember that the stream is lazy, so it will only call new rejections
* while the person isn't rejected.
*/
public Function<Person, Person> shortCircuitReject(List<Function<Person, Person>> rejections) {
return person -> rejections.stream()
.map(rejection -> rejection.apply(person))
.filter(Person::isRejected)
.findFirst()
.orElse(person);
}
Now your stream can look like this:
List<Function<Person, Person>> rejections = Arrays.asList(
App::rejectIfYoungerThan15,
App::rejectIfNameStartsWithD);
List<Person> persons1 = persons.stream()
.map(shortCircuitReject(rejections))
.collect(Collectors.toList());
Related
I'm writing a function to check multiple conditions in an array, if they are all true then return true.
For example:
public class Attribute {
private final String key;
private final String value;
//...
}
boolean canContactDogOwner(List<Attribute> attributes) {
boolean hasDog = false;
boolean isSubscribed = false;
boolean isOkToCall = false;
for (var attribute : attributes) {
if (attribute.key().equals("dogName")) {
hasDog = true;
} else if (attribute.key().equals("isSubscribed") && attribute.value().equals("Y")) {
isSubscribed = true;
} else if (attribute.key().equals("okToCall") && attribute.value().equals("Y")) {
isOkToCall = true;
}
// 1.
}
return hasDog && isSubscribed && isOkToCall;
}
void foo() {
List<Attribute> attributes = new ArrayList<>();
attributes.add(new Attribute("isSubscribed", "Y"));
attributes.add(new Attribute("okToCall", "Y"));
attributes.add(new Attribute("mobile", "12345678"));
attributes.add(new Attribute("landline", "1346346"));
attributes.add(new Attribute("email", "white#email.com"));
attributes.add(new Attribute("dogName", "Alex"));
boolean canContact = canContactDogOwner(attributes);
}
Two questions:
When all conditions are meet, the loop can be break, but if I add a check there, we would be checking every step in the loop, which doesn't look good. Any suggestions?
Is there a better / concise way to do it?
Like following?
boolean canContactDogOwner(List<Attribute> attributes) {
return attributes.stream().allMatch(A,B,C);
}
You can modify method canContactDogOwner to be like this,
boolean canContactDogOwner(List<Attribute> attributes) {
List<Attribute> conditions = new ArrayList<>();
conditions.add(new Attribute("isSubscribed", "Y"));
conditions.add(new Attribute("okToCall", "Y"));
return attributes.containsAll(conditions) &&
attributes.stream().anyMatch((attribute -> attribute.key.equals("dogName")));
}
A working and cleaner approach (IMO) will be to use some abstract data type like Map in this case..
static boolean canContactDogOwner(List<Attribute> attributes){
Map<String, String> attributeMap = new HashMap<>(); // empty map
attributes.forEach(attr -> attributeMap.put(attr.getKey(), attr.getValue())); // populate map
return attributeMap.containsKey("dogName") &&
"Y".equals(attributeMap.get("isSubscribed")) &&
"Y".equals(attributeMap.get("okToCall")); // Constant-String-first on equals check to avoid nullPointerExc with less code, yet clean
}
The code above with the comment is self-explanatory, so not adding details of the code.
But it is worth mentioning that
the complexity is still O(n) like other solutions here, n - number of elements (attribute objects)
flexibility to add or remove more conditions in the return statement
map as a chosen data-type and <Constant>.equals check avoids key validation and nullPointerException respectively.
If you are fascinated with Java-Streams, you can modify the code like this too..
static boolean canContactDogOwner(List<Attribute> attributes){
Map<String, String> attributeMap = attributes.stream()
.collect(Collectors.toMap(Attribute::getKey, Attribute::getValue));
return attributeMap.containsKey("dogName") &&
"Y".equals(attributeMap.get("isSubscribed")) &&
"Y".equals(attributeMap.get("okToCall"));
}
You could check if all condition is meet only when you set a value to true,
it will happen only 3 time.
And more concise way, probably with stream().anyMatch() but i'm not sure it will be more readable
Stream and allMatch(Predicate predicate) is a better way to do it in my opinion, but keep in mind that allMatch() take a Predicate as an argument, so you need to provide one.
I would suggest you encapsulate the attributes and create a class
something like Owner.
public class Owner {
private boolean isSubscribed;
private boolean okToCall;
private String mobile;
private String landline;
private String email;
private Optional<String> dogName;
public Owner(boolean isSubscribed, boolean okToCall, String mobile, String landline, String email, Optional<String> dogName) {
this.isSubscribed = isSubscribed;
this.okToCall = okToCall;
this.mobile = mobile;
this.landline = landline;
this.email = email;
this.dogName = dogName;
}
public boolean canContact() {
return this.isSubscribed && this.okToCall;
}
public boolean hasDog() {
return dogName.isPresent();
}
}
This way you do not have to deal with the if loops, the Owner object will say if they have a dog and can be contacted, etc.
public static void main(String[] args) {
Owner owner = new Owner(true, true, "12345678", "1346346", "white#email.com", Optional.of("Alex"));
boolean canContact = owner.hasDog() && owner.canContact();
}
I think you can have two lists of your conditions and attributes and then check whether attributes contain all condition or not.
public static Boolean allConditionsExist(List<String> attributes, List<String> conditions) {
return attributes.containsAll(conditions);
}
To convert your conditions and attributes to a list you can do something like this.
List<String> conditions = Arrays.asList("dogName","isSubscribed", "okToCall"); // add all your conditions
and
List<String> attributeKeys = attributes.stream().map(Attribute::getKey).collect(Collectors.toList());
Then call
allConditionExist(attributeKeys, conditions);
Assuming that every attribute is present only once, you could write
boolean canContactDogOwner(List<Attribute> attributes) {
int matches = 0;
for (var attribute : attributes) {
if (attribute.key().equals("dogName")) ||
attribute.key().equals("isSubscribed") && attribute.value().equals("Y") ||
attribute.key().equals("okToCall") && attribute.value().equals("Y"))
{
matches++;
if (matches >= 3) {
return true;
}
}
}
return false;
}
For the stream way you could write a Collector, constructed with a list of Predicates and returning a boolean. Wouldn't be the fastest...
Something like:
public class AllMatch<T> implements Collector<T, Set<Predicate<T>>, Boolean>
{
private Set<Predicate<T>> filter;
public AllMatch(Predicate<T>... filter)
{
super();
this.filter = new HashSet(Arrays.asList(filter));
}
#Override
public Supplier<Set<Predicate<T>>> supplier()
{
return () -> new HashSet<>();
}
#Override
public BinaryOperator<Set<Predicate<T>>> combiner()
{
return this::combiner;
}
#Override
public Set<Characteristics> characteristics()
{
return Stream.of(Characteristics.UNORDERED).collect(Collectors.toCollection(HashSet::new));
}
public Set<Predicate<T>> combiner(Set<Predicate<T>> left, Set<Predicate<T>> right)
{
left.addAll(right);
return left;
}
public Set<Predicate<T>> accumulator(Set<Predicate<T>> acc, T t)
{
filter.stream().filter(f -> f.test(t)).forEach(f ->
{
acc.add(f);
});
return acc;
}
#Override
public Function<Set<Predicate<T>>, Boolean> finisher()
{
return (s) -> s.equals(filter);
}
#Override
public BiConsumer<Set<Predicate<T>>, T> accumulator()
{
return this::accumulator;
}
public static void main(String[] args) {
Integer[] numbers = {1,2,3,4,5,6,7,8};
System.out.println(Arrays.stream(numbers).collect(new AllMatch<Integer>((i)-> i.equals(5),(i)-> i.equals(6))));
System.out.println(Arrays.stream(numbers).collect(new AllMatch<Integer>((i)-> i.equals(5),(i)-> i.equals(9))));
}
}
I have a method with which I would like to check if, in my model, enum of some type exists.
If it exists I want to return true an if not I would like to return false with error message.
I have model that looks like this:
#Data
public class DataResponse {
public final String userId;
public final List<Model> modelList;
}
Model.java
#Data
public class Model {
public final String modelId;
public final ModelType type;
public final ModelStatus status;
}
ModelType.java
public enum ModelType {
Fast,
Slow;
}
ModelStatus.java
public enum ModelStatus {
CREATED,
FAILED
}
Now I would like to check using strems if my model contains type "Fast" and if contains status "CREATED" if yes return true if not return false with error message.
So far I have this:
public Boolean isModelFastAndSuccess(String modelId){
Optional<DataResponse> modelList = dataService.getModelStatus(modelId);
userProofList.stream().map(ml -> ml.modelList)
.map(models -> {
if()
})
.collect(Collectors.toList());
}
I am not sure how to finish it.
Any advice appreciated.
The approach to implementation of isModelFastAndSuccess could be as follows assuming that dataService::getModelStatus returns Optional<DataResponse>:
public Boolean areAllModelsFastAndSuccess(String modelId) {
return dataService.getModelStatus(modelId) // Optional<DataResponse>
.map(DataResponse::getModelList) // Optional<List<Model>>
.orElse(Collections.emptyList()) // List<Model>
.stream() // Stream<Model>
.allMatch(model -> model.getType() == ModelType.Fast
&& model.getStatus() == ModelStatus.CREATED
);
}
Also, a method may be implemented to return true if there is at least one Fast / Created model:
public Boolean isAnyModelFastAndSuccess(String modelId) {
return dataService.getModelStatus(modelId) // Optional<DataResponse>
.map(DataResponse::getModelList) // Optional<List<Model>>
.orElse(Collections.emptyList()) // List<Model>
.stream() // Stream<Model>
.anyMatch(model -> model.getType() == ModelType.Fast
&& model.getStatus() == ModelStatus.CREATED
);
}
I have multiple Optionals that must be mapped to a POJO. Is there a better alternative than the following?
class SimplePojo {
private String stringField;
private Integer integerField;
// All args. constructor, getter, setter
}
Optional<String> stringOptional = ...
Optional<Integer> integerOptional = ...
Optional<SimplePojo> simplePojoOptional = stringOptional.flatMap(
string -> integerOptional.map(integer -> new SimplePojo(string, integer)))
I have reduced the problem to 2 Optionals in the above example to keep it short. But I actually have 3 Optionals with more on the way. I am afraid the last line can easily become unwieldy soon.
Please note: Use of functional frameworks like Vavr or Functional Java is not an option for me.
How about using a Builder ?
class SimplePojo {
public static class Builder {
private String stringField;
public Builder withStringField(String str) {
this.stringField = str;
return this;
}
// and other "with" methods...
public Optional<SimplePojo> build() {
if (stringField == null || anotherField == null /* and so forth */) {
return Optional.empty();
} else {
return Optional.of(new SimplePojo(this));
}
}
}
private final String stringField;
/* private constructor, so client code has to go through the Builder */
private SimplePojo(Builder builder) {
this.stringField = builder.stringField;
// etc.
}
}
Then you could use it as follows:
SimplePojo.Builder builder = new SimplePojo.builder();
optionalStringField.ifPresent(builder::withStringField);
// etc.
return builder.build();
I do not see any advantage from pursuing the functional style this way here. see three options:
ONE: If you can alter the SimplePojo class and if this scenario is a common one, you might consider to add a factory method to the SimplePojo:
class SimplePojo {
public static Optional<SimplePojo> of(final Optional<String> stringField, final Optional<Integer> integerField) {
if (stringField.isPresent() && integerField.isPresent()) {
return new SimplePojo(stringField.get(), integerField.get());
else
return Optional.empty();
}
}
TWO: If you cannot alter the SimplePojo, you might want to create this as a utility method somewhere else. If you need this pattern only in one class, make the method private in this class!
THREE: If you need to do this only once or twice, I would prefer the if...then construction from the first option over the functional notation you used for the sake of readability:
final Optional<SimplePojo> simplePojoOptional;
if (stringField.isPresent() && integerField.isPresent()) {
simplePojoOptional = new SimplePojo(stringField.get(), integerField.get());
else
simplePojoOptional = Optional.empty();
I was reading the article about validation using Predicates here. I am trying to implement it in Spring Boot framework where I am having some questions.
In the code:
public class LamdaPersonValidator implements PersonValidator {
public void validate(Person person) {
notNull.and(between(2, 12)).test(person.getFirstName()).throwIfInvalid("firstname");
notNull.and(between(4, 30)).test(person.getLastName()).throwIfInvalid("secondname");
notNull.and(between(3, 50)).and(contains("#")).test(person.getEmail()).throwIfInvalid("email");
intBetween(0, 110).test(person.getAge()).throwIfInvalid("age");
}
}
it is not mentioned on what could be the standard way to check if the person object in the validate method is itself is null. Is it OK to just put a null check like if(persone != null) { // notNull.and..} or there could be some better way to do null check.
Another thing is suppose, I want to do some custom checks like if person exists in the database or not. In this case, I need to connect to the database to check so. In this case, I need to Autowire the interface where static variable and method is not possible.
So, what could be best approach to use this when doing validation from the database?
We are not the code judges of the holy inquisition, so it’s not our duty to tell you, whether it is “OK to just put a null check”.
Of course, it is ok to write is as an ordinary if statement, like we did the last 25 years, just like it is ok to invent a verbose framework encapsulating the null check and bringing the term “lambda” somehow into it. The only remaining question would be if you really intent to write if(person != null) { /* do the checks */ }, in other words, allow a null person to pass the test.
In case, you want to reject null persons (which would be more reasonable), there is already a possibility to write it without an explicit test, Objects.requireNonNull, since Java 7, which demonstrates that you don’t need an “everything’s better with lambdas” framework to achieve that goal. Generally, you can write validating code reasonably with conventional code, contrary to the article’s example, utilizing simple tools like the && operator and putting common code into methods:
public void validate(Person person) {
Objects.requireNonNull(person, "person is null");
checkString(person.getFirstName(), "first name", 2, 12);
checkString(person.getLastName(), "last name", 4, 30);
checkString(person.getEmail(), "email", 3, 50);
if(!person.getEmail().contains("#"))
throw new IllegalArgumentException("invalid email format");
checkBounds(person.getAge(), "age", 0, 110);
}
private void checkString(String nameValue, String nameType, int min, int max) {
Objects.requireNonNull(nameValue, () -> nameType+" is null");
checkBounds(nameValue.length(), nameType, min, max);
}
private void checkBounds(int value, String valueType, int min, int max) {
if(value < min || value > max)
throw new IllegalArgumentException(valueType+" is not within ["+min+" "+max+']');
}
This does the same as your code, without any framework with “Lambda” in its name, still having readable validation code and allowing to reuse the checking code. That said, instead of a class name LamdaPersonValidator, which reflects how you implemented it, you should use class names reflecting the responsibilities of a class. Clearly, a validator responsible for validating some properties of an object should not get mixed up with a validator checking the presence of an entity in the database. The latter is an entirely different topic on its own and should also be in a question on its own.
The code above is only meant to be an example how to achieve the same as the original code. It should never appear in production code in this form, as it is a demonstration of a widespread anti-pattern, to apply arbitrary unreasonable constraints to properties, most likely invented by the programmer while writing the code.
Why does it assume that a person must have a first name and a last name and why does it assume that a first name must have at least two and at most twelve characters, while the last name must be between four and thirty characters?
It’s actually not even characters, as the association between char units and actual characters is not 1:1.
A must read for every programmer thinking about implementing name validation, is Falsehoods Programmers Believe About Names (With Examples).
Likewise, Wikipedia’s List of the verified oldest people lists one hundred people having an age above 110.
And there is no reason to assume that an email address can’t have more than fifty characters. A true validation of the correct Email pattern may turn out to be something to omit deliberately…
You can write GenericValidator like that also:
Write AbstractValidator class for common work:
public abstract class AbstractValidator {
private Map<Predicate, String> validatorMap = new LinkedHashMap<>();
protected List<String> messages;
public AbstractValidator() {
this.messages = new ArrayList<>();
}
protected <E> AbstractValidator add(Predicate<E> predicate, String reason) {
validatorMap.put(predicate, reason);
return this;
}
protected AbstractValidator apply(String fieldName, Object val) {
AtomicBoolean flag= new AtomicBoolean(true);
this.validatorMap.forEach((modifier, reason) -> {
if (flag.get() && !modifier.test(val)) {
String message = MessageFormat.format("{0} {1}", fieldName, reason);
messages.add(message);
flag.set(false);
}
});
this.validatorMap.clear();
return this;
}
public void end(String exceptionStatus) {
Optional.ofNullable(messages).filter(CollectionUtils::isEmpty)
.orElseThrow(() -> {
RuntimeException ex = new RuntimeException(exceptionStatus, messages);
messages.clear();
return ex;
});
}
}
Write GenericValidator class which will extend the AbstractValidator for your validation implementation:
public class GenericValidator extends AbstractValidator {
private GenericValidator() {
super();
}
public static GenericValidator of() {
return new GenericValidator();
}
public GenericValidator nonNull() {
add(Objects::nonNull, "Field value is null");
return this;
}
public GenericValidator notEmpty() {
add(StringUtils::isNotEmpty, "Field is empty");
return this;
}
public GenericValidator min(int min) {
add(s -> ((String) s).length() >= min, "Field min size is " + min);
return this;
}
public GenericValidator max(int max) {
add(s -> ((String) s).length() <= max, "Field max size is " + max);
return this;
}
public GenericValidator notEmptyList() {
add(CollectionUtils::isNotEmpty, "Field List is null/Empty");
return this;
}
public GenericValidator apply(String fieldName, Object val) {
return (GenericValidator) super.apply(fieldName, val);
}
}
Please test accordingly. Example for test cases:
class GenericValidatorTest {
#Test
void genericValidationSuccessCase() {
Abc abc = new Abc();
abc.setName("a");
abc.setVal(1);
abc.setAbslist(Collections.singletonList(new ChildAbc()));
GenericValidator of = GenericValidator.of();
of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);
of.notEmpty().min(1).max(1).apply("name", abc.getName())
.nonNull().apply("value", abc.getVal())
.notEmptyList().apply("childAbc", abc.getAbslist())
.end(GENERIC_JSON_SERIALIZATION);
}
#Test
void genericValidationWhenObjectNull() {
GenericValidator of = GenericValidator.of();
Assertions.assertThrows(BusinessException.class, () -> of.nonNull()
.apply("abc", null).end(GENERIC_JSON_SERIALIZATION));
}
#Test
void genericValidationWithExceptionInput() {
Abc abc = new Abc();
abc.setName("a");
abc.setVal(1);
GenericValidator of = GenericValidator.of();
of.nonNull().apply("abc", abc).end(GENERIC_JSON_SERIALIZATION);
GenericValidator apply = of.notEmpty().min(1).max(1).apply("name", abc.getName())
.nonNull().apply("value", abc.getVal())
.notEmptyList().apply("childAbc", abc.getAbslist());
Assertions.assertThrows(BusinessException.class, () -> apply.end(GENERIC_JSON_SERIALIZATION));
}
}
class Abc {
String name;
Integer val;
List<ChildAbc> abslist;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getVal() {
return val;
}
public void setVal(Integer val) {
this.val = val;
}
public List<ChildAbc> getAbslist() {
return abslist;
}
public void setAbslist(List<ChildAbc> abslist) {
this.abslist = abslist;
}
}
class ChildAbc {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
My goal is to apply 2 sorts on a list of objects made of string and boolean.
I have accounts and active/inactive states, so I want to show active first (sort boolean values) and then to sort rest of elements in alphabetical order.
For instance:
[John,inactive], [Craig,active], [Mike,inactive], [Dennis,innactive]
I want to have:
[Craig,active], [Dennis,innactive], [John,inactive], [Mike,inactive]
What I plan to do is to use Comparable<> but I wonder if there is another way to do that.
I don't want to use Guava or any additional libraries.
This should be used for Android API 21 as well, so list.sort() cannot be used.
Thanks in advance!
Simply create a new Comparator like this:
public class AccountComparator implements Comparator<Account> {
#Override
public int compare(Account o1, Account o2) {
if (o1.isActive() && !o2.isActive()) {
return -1;
}
if (!o1.isActive() && o2.isActive()) {
return 1;
}
return o1.getName().compareTo(o2.getName());
}
}
Minimal tested example:
public static void main(String[] args) {
Account account2 = new Account("B", true);
Account account4 = new Account("D", false);
Account account3 = new Account("C", true);
Account account1 = new Account("A", false);
List<Account> list = new ArrayList<>();
list.add(account1);
list.add(account2);
list.add(account3);
list.add(account4);
Collections.sort(list, new AccountComparator());
list.forEach(System.out::println);
}
with the expected output of
Account{name='B', active=true}
Account{name='C', active=true}
Account{name='A', active=false}
Account{name='D', active=false}
Or with a lambda expression: (Thanks to #Wow for using Comparator.comparing)
Collections.sort(list, Comparator.comparing(Account::isActive).reversed()
.thenComparing(Account::getName));
There's no magic/easy way to do it without Java 8 or some third-party library. You'll have to implement Comparable and do the heavy lifting yourself:
public class Person implements Comparable<Person> {
private final boolean isActive;
private final String name;
#Override
public int compareTo(Person other) {
if (isActive && !other.isActive) {
return -1;
} else if (!isActive && other.isActive) {
return 1;
} else {
return name.compareTo(other.name);
}
}
}