I have the following utility function for checking if a String
variable is empty or null:
bool isEmpty(String s){
return (s??'').isEmpty;
}
Now I want to make a similar function for Iterable
s. The implementation is trivial:
bool isEmpty(Iterable i){
return (i??[]).isEmpty;
}
But now I have to either name the two functions differently, or somehow merge them into one. Here's where I run into trouble.
I can make the variable dynamic
:
bool isEmpty(dynamic x){
if( x is String) return (x??'').isEmpty;
if( x is Iterable) return (x??[]).isEmpty;
throw UnimplementedError('isEmpty() is not defined for the type ${x.runtimeType}');
}
but then x will be of type Null
if I pass String s = null
or Set s = null
. What if in the future I want to treat null
differently for Iterable
and String
?
I could make the function generic:
bool isEmpty<T>(T x){
if( T == String) return ((x as String)??'').isEmpty;
if( T == Iterable) return ((x as Iterable)??[]).isEmpty;
throw UnimplementedError('isEmpty() is not defined for the type $T');
}
but now it will throw an exception if I pass a List
, Set
or anything else that is a subtype of Iterable
, but not an actual Iterable
.
How can I make one single isEmpty()
function that works exactly as the two separate ones that accept String
and Iterable
?
You can make extensions. Like these:
extension StringExt on String {
bool isNullOrEmpty() => this == null || this.isEmpty;
}
extension IterableExt<T> on Iterable<T> {
bool isNullOrEmpty() => this == null || this.isEmpty;
}
I've renamed isEmpty
to isNullOrEmpty
due to name conflicts (String.isEmpty, Iterable<T>.isEmpty
...).