I have a Test
class and a Writer
class.
The Writer
class does nothing but writes a string to a StringBuilder
, nothing more. It also is allowed to have "transformers". Meaning, in the constructor of the Writer
class, you can pass any number of lambda functions to add additional processing to a string you are writing.
For example, like String::toLowerCase
.
I want to be able to pass multiple of these "transformers" into the constructor of the writer.
I have the below code:
///Writer Class Writer(Function<String, String>... someFunctions) {
abstract class Writer {
Function<String, String>[] functions;
Writer(Function<String, String>... someFunctions) {
functions = someFunctions;
}
}
///Test Class
@Test
default void WriterTestWriteThere() throws IOException {
Writer writer = createWriter();
writer.write("There");
writer.close();
assertEquals("There", getContents());
}
@Test
default void WriterTestTwoTransformerFunctions() throws IOException {
Writer writer = createWriter(
text -> text.replaceAll("sometext", "s*****"),
String::toUpperCase
);
writer.write("This is somethext!");
writer.close();
assertEquals("THIS IS S*****!", getContents());
}
However, the compiler issues multiple warnings.
Possible heap pollution from parameterized vararg type
- error-message for the constructor of the Writer
class.
Unchecked generics array creation for varargs parameter
- error-message for the Test
class when the Writer
instance is being created.
I cannot use @SafeVarargs or @SuppressWarnings
I have looked everywhere online, but cannot get a solid answer as to why these warnings are created, other than that the Java compiler does not like parameterized arguments being passed into a list. So, raises my question.
What can I use to fix it?
I need to be able to pass multiple functions into the writer class to transform the text as those functions want, and have no warnings. I have experimented with other Java utility classes other than Function, but I can't seem to find what I need.
CodePudding user response:
I cannot use @SafeVarargs or @SuppressWarnings
As you probably know, arrays and generics don't play well together.
Arrays are covariant, i.e. method specifies Number[]
- you can pass as an argument Integer[]
, Double[]
, BigInteger[]
, etc.
Generics are invariant, i.e. if the declared parameter is a List<Number>
- you can pass only List<Number>
.
When you are passing an argument into the method that expects varargs they will be wrapped by an array. Therefore, the compiler isn't able to inure type-safety when you're mixing varargs and generics, and it warns you about possible heap pollution since it's impossible to verify whether the type of arguments will match.
You have to eliminate the need to create a generic array.
The best option is to combine all functions into a single one before passing into a constructor of Writer
.
Function<String, String> f1 = String::toUpperCase;
Function<String, String> f2 = text -> text.replaceAll("somethext", "s*****");
Function<String, String> f3 = text -> text.replaceAll("\\p{Punct}", "&");
Function<String, String> combined = f1.andThen(f2).andThen(f3);
And Writer
should have a field of type Function<String, String>
but not, an array of functions.
public class Writer {
private Function<String, String> function;
public Writer(Function<String, String> function) {
this.function = function;
}
}