CodeGym /Courses /Java Core /BufferedInputStream

BufferedInputStream

Java Core
Level 8 , Lesson 6
Available

"Hello, Amigo! Today I'll tell you a few interesting things about the BufferedInputStream class, but let's start with «wrappers» and a «bag of sugar»."

"What do you mean by «wrapper» and «bag of sugar»?"

"These are metaphors. Listen. So…"

The «wrapper» (or «decorator») design pattern is a fairly simple and convenient mechanism for extending object functionality without using inheritance.

BufferedInputStream - 1

Suppose we have a Cat class with two methods: getName and setName:

Java code Description
class Cat
{
 private String name;
 public Cat(String name)
 {
  this.name = name;
 }
 public String getName()
 {
  return this.name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
}
The Cat class has two methods: getName and setName
public static void main(String[] args)
{
 Cat cat = new Cat("Oscar");

 printName(cat);
}

public static void printName(Cat cat)
{
 System.out.println(cat.getName());
}
An example of how it might be used.

«Oscar» will be displayed on the console.

Suppose we need to intercept a method call on a cat object and perhaps make some small changes. For this, we need to wrap it in its own wrapper class.

If we want to "wrap" our own code around the method calls on some object, then we need to:

1) Create our own wrapper class and inherit from the same class/interface as the object to be wrapped.

2) Pass the object to be wrapped to our class's constructor.

3) Override all methods in our new class. Invoke the wrapped object's methods inside each of the overridden methods.

4) Make whatever changes you want: change what the method calls do, change their parameters, and/or do something else.

In the example below, we intercept calls to a Cat object's getName method and slightly change its return value.

Java code Description
class Cat
{
 private String name;
 public Cat(String name)
 {
  this.name = name;
 }
 public String getName()
 {
  return this.name;
 }
 public void setName(String name)
 {
  this.name = name;
 }
}
The Cat class contains two methods: getName and setName.
class CatWrapper extends Cat
{
 private Cat original;
 public CatWrapper (Cat cat)
 {
  super(cat.getName());
  this.original = cat;
 }

 public String getName()
 {
  return "A cat named " + original.getName();
 }

 public void setName(String name)
 {
  original.setName(name);
 }
}
The wrapper class. The class doesn't store any data except a reference to the original object.
The class is able to "throw" calls to the original object (setName) passed to the constructor. It can also "catch" these calls and modify their parameters and/or results.
public static void main(String[] args)
{
 Cat cat = new Cat("Oscar");
 Cat catWrap = new CatWrapper (cat);
 printName(catWrap);
}

public static void printName(Cat named)
{
 System.out.println(named.getName());
}
An example of how it might be used.

«A cat named Oscar».
will be displayed on the console

In other words, we quietly replace each original object with a wrapper object, which receives a link to the original object. All method calls on the wrapper are forwarded to the original object, and everything runs like clockwork.

"I like it. The solution is simple and functional."

"I'll also tell you about a «bag of sugar». This is a metaphor rather than a design pattern. A metaphor for the word buffer and buffering. What is buffering and why do we need it?"

BufferedInputStream - 2

Let's say that today it's Rishi's turn to cook, and you're helping him. Rishi isn't here yet, but I want to drink tea. I ask you to bring me a spoonful of sugar. You go to the basement and find a bag of sugar. You can bring me the entire bag, but I don't need the bag. I only need one spoonful. Then, like a good robot, you take one spoonful and bring it to me. I add it to the tea, but it still isn't sweet enough. And I ask you to bring me one more. You again go to the basement and bring another spoonful. Then Ellie comes along, and I ask you to bring sugar for her... All this takes too long and is inefficient.

Rishi comes, sees all this, and asks you to bring him a sugar bowl full of sugar. Then Ellie and I start asking Rishi for sugar. He simply serves it to us from the sugar bowl, and that's all.

What happened after Rishi showed up is called buffering: the sugar bowl is a buffer. Thanks to buffering, "clients" can read data from a buffer in small portions, while the buffer, in order to save time and effort, reads them from the source in large portions.

"That's a cool example, Kim. I understand perfectly. The request for a spoonful of sugar is like reading one byte from a stream."

"Exactly. The BufferedInputStream class is a classical example of a buffered wrapper. It wraps the InputStream class. It reads data from the original InputStream in large blocks into a buffer, and then pulls it out of the buffer piece-by-piece as we read from it."

"Very good. It's all clear. Are there buffers for writing?"

"Oh, sure."

"Maybe an example?"

"Imagine a trash can. Instead of going outside to put trash into an incinerator every time, you just throw it into the trash can. Then Bubba takes the can outside once every two weeks. A classic buffer."

"How interesting! And a lot clearer than a bag of sugar, by the way."

"And the flush() method is like taking out the garbage right away. You can use it before guests arrive."

Comments (28)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Krig Raseri Level 38, Dallas, United States
9 March 2023
Krig is confused. Also great work keeping the lesson score at 69
Tasmoda Level 28, Midrand, South Africa
28 July 2022
This lesson is too sweet😂
Alexandru Level 22, High Blantyre, United Kingdom
22 October 2021
In the example it says that a wrapper class extends an object's functionality without using inheritance. why then the CatWrapper class extends Cat?
Lisa L Level 47, Nuremberg, Germany
29 April 2022
The wording is OK. The functionality is extended using aggregation and not inheritance. If you remove inheritance it'll still work as expected. Inheritance is only used so that you can continue working with the original object. Original obj = new Wrapper(new Original()); without inheritance you'd have to work with a Wrapper reference and eventually you'd have to change existing code. Extending the original gives you the possibility to change your mind at any time and replace an object with a object wrapping the original one without the need to adapt code. Wrapper obj = new Wrapper(new Original());
TheLordJackMC Level 39, Princeton, idk somewhere
18 July 2021
me, chugging the sugar:
Diego deFontenelle Level 27, Rennes, France
9 March 2021
Hi, Can someone help me to understand, why "wrapper" is presented concurrently with "BufferedInputStream". Thanks in advance.
Gellert Varga Level 23, Szekesfehervar, Hungary
25 June 2021
I think it's just so we can finally understand better how it works that you can put constructors of different InputStream classes together into each other like legos. For example:

BufferedInputStream bfrInp = new BufferedInputStream(new FileInputStream(fileName));
"The BufferedInputStream class is a classical example of a buffered wrapper. It wraps the InputStream class."
Diego deFontenelle Level 27, Rennes, France
28 June 2021
Thanks for your answer. Best regards
Krig Raseri Level 38, Dallas, United States
2 March 2023
This is mega late, but for anyone that stumbles upon this it is because bufferedinputstream is a wrapper of inputstream.
Andrei Level 41
14 January 2021
Man this was a sweet lesson but could've come a lot earlier, especially the part about Buffer and Buffering! 🤓😁
Onur Bal Level 27, Istanbul, Turkey
22 September 2020
In the constructor of CatWrapper

public CatWrapper (Cat cat)
 {
  super(cat.getName());
  this.original = cat;
 }
What is the purpose of calling the Cat class's constructor, when we already have a cat instance which we will be assigning to "original"? I suppose it means that our CatWrapper class now has a "private String name" field, but there seems to be no point since we never access it.
Seferi Level 22, United Federation of Planets
10 October 2020
I'm wondering this as well.
 
super(cat.getName());
 
is not assigned to anything and never accessed...
Onur Bal Level 27, Istanbul, Turkey
10 October 2020
Hey, Seferi, I actually later on found the answer to this question. As it turns out, since CatWrapper extends Cat, inside the CatWrapper's constructor, it is obligatory to call the constructor of the parent class, i.e. that of Cat. Even if you do not state this explicitly, the Java Machine does it for you. So you can imagine the Java Machine inserting a "super()" line at the very beginning of your constructor. But the issue here is that the Cat class has no default constructor, it only has "public Cat(String name)", which means that "super()" is not valid, which in turn makes the programme give an error. In order to prevent this error, we must call the super constructor ourself, with the required parameter, which is why we end up writing "super(cat.getName())", even though it may feel useless at a glance for the functioning of our wrapper class.
Seferi Level 22, United Federation of Planets
11 October 2020
Now I get it. Thanks a lot mate!
Gellert Varga Level 23, Szekesfehervar, Hungary
25 June 2021
I think it was a not too fortunate choice by CodeGym that CatWrapper inherits from Cat. Following the first rule described by CodeGym: "1) Create our own wrapper class and inherit from the same class/interface as the object to be wrapped", - I would rather code the following class structure:

abstract class Animal { . . . }
class Cat extends Animal { . . . }
class CatWrapper extends Animal { . . . }
Now Cat is not the superclass of CatWrapper, so there would be no need for this command in the CatWrapper constructor: super(cat.getName()) - which I think is pointless too (although necessary in the lesson example-code, as Onur Bal wrote.)
yehuda b Level 23, Beersheba, Israel
19 August 2020
I wonder if the CatWrapper constructor's call to super(cat.getName()), is only there because, being that Cat doesn't have a default constructor, when defining CatWrapper's original field through CatWrapper's constructor, you have to call the parent class's constructor. Which makes me wonder why not name the CatWrapper object cat.getName() + " 's Wrapper "?
Fadi Alsaidi Level 34, Carrollton, TX, USA
9 March 2020
that first picture up there is making me very uncomfortable.
Robert Constantinescu Level 25, Bucharest, Romania
30 November 2019
Nice one !