I've just learned about custom wrapper classes and I'm a bit confused about a couple of things. First, what's the utility of such tool? From what I understand you can mimic the copied class behaviour but with slight modifications. Is this tool used often?
Also, could you please have a look at my code? I know I'm suppose to close streams with "try with resources", but I'm not sure where it fits inside a constructor. Any other suggestions about the quality of my code are appreciated, please tear it apart!
/*
UnsupportedFileName
Change the TxtInputStream class so that it only works with txt files (* .txt).
For example, first.txt or name.1.part3.txt.
If a non-txt file is passed (e.g. file.txt.exe), then the constructor should throw
an UnsupportedFileNameException.
Think about what else you need to do if an exception is thrown.
Requirements:
x 1. The TxtInputStream class must inherit the FileInputStream class.
2. If a txt file is passed to the constructor, TxtInputStream should behave like a regular FileInputStream.
3. If a non-txt file is passed to the constructor, an UnsupportedFileNameException should be thrown.
4. If an exception is thrown, then you must also call super.close().
*/
public class TxtInputStream extends FileInputStream {
private FileInputStream original;
public static String fileName;
public TxtInputStream(String fileName) throws IOException, UnsupportedFileNameException {
super(fileName);
if(fileName.substring(fileName.length()-4).equals(".txt")) {
this.fileName = fileName;
} else {
super.close();
throw new UnsupportedFileNameException();
}
}
public static void main(String[] args) {
}
}
1 Answer 1
The cleanest way to solve this would probably be to check the argument before calling the super constructor. This allows the code to fail fast and prevents it from opening the file, rendering the close()
call unnecessary:
public class TxtInputStream extends FileInputStream {
public TextInputStream(String fileName) {
// super call must be first statement so have to call validation method
// as argument to super call
super(checkFileName(fileName));
}
private static String checkFileName(String fileName) {
if (!fileName.endsWith(".txt")) {
throw new UnsupportedFileNameException();
}
return fileName;
}
}
However, this is a pretty bad example for wrappers since the TxtInputStream
does not add any functionality at all.
If you want to see some good examples have at look at which JDK classes implement InputStream
:
BufferedInputStream
: Buffers data, this can be useful when you are only consuming small amounts of data at a time, but reading such small amounts of data would be inefficient, e.g. for a network connection. Instead it reads larger chunks from the wrapped InputStream and when you then consume data it first serves it from the buffer.CipherInputStream
: Decrypts the dataInflaterInputStream
: Uncompresses data
So assuming you read encrypted, compressed data over the network you could have:
InputStream networkInputStream = ...
BufferedInputStream bufferedInputStream = new BufferedInputStream(networkInputStream);
CipherInputStream cipherInputStream = new CipherInputStream(bufferedInputStream, cipher);
InflaterInputStream inflaterInputStream = new InflaterInputStream(cipherInputStream);
Then you can simply use inflaterInputStream
like any other InputStream
and do not even have to worry about all the previous processing steps which are taking place.
Exception
to be thrown. \$\endgroup\$null
) might be acceptable but usually the constructor arguments should be checked before calling the constructor. Especially when talking about DTOs this can dramatically simplify unit test configurations where you often don't deed these DTOs having consistent configuration. Business classes should not have constructor parameters that need to be consistent beyond not beingnull
anyway. ;o) \$\endgroup\$