Skip to content

Commit c783714

Browse files
authored
Merge pull request #3 from m0rk4/classes-and-interfaces-4
Classes and interfaces 4
2 parents ca6e2cd + ccd1c7c commit c783714

File tree

14 files changed

+434
-0
lines changed

14 files changed

+434
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package by.morka.effective.java.classesandinterfaces.compositionoverinheritance;
2+
3+
import java.util.Collection;
4+
import java.util.Iterator;
5+
import java.util.Set;
6+
7+
// Reusable forwarding class, simply forwards methods, api is strong and independent from impl.
8+
/*
9+
Important fact is that it IMPLEMENTS, not extends
10+
*/
11+
public class ForwardingSet<E> implements Set<E> {
12+
private final Set<E> s;
13+
public ForwardingSet(Set<E> s) { this.s = s; }
14+
15+
public void clear() { s.clear(); }
16+
public boolean contains(Object o) { return s.contains(o); }
17+
public boolean isEmpty() { return s.isEmpty(); }
18+
public int size() { return s.size(); }
19+
public Iterator<E> iterator() { return s.iterator(); }
20+
public boolean add(E e) { return s.add(e); }
21+
public boolean remove(Object o) { return s.remove(o); }
22+
public boolean containsAll(Collection<?> c)
23+
{ return s.containsAll(c); }
24+
public boolean addAll(Collection<? extends E> c)
25+
{ return s.addAll(c); }
26+
public boolean removeAll(Collection<?> c)
27+
{ return s.removeAll(c); }
28+
public boolean retainAll(Collection<?> c)
29+
{ return s.retainAll(c); }
30+
public Object[] toArray() { return s.toArray(); }
31+
public <T> T[] toArray(T[] a) { return s.toArray(a); }
32+
@Override public boolean equals(Object o)
33+
{ return s.equals(o); }
34+
@Override public int hashCode() { return s.hashCode(); }
35+
@Override public String toString() { return s.toString(); }
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package by.morka.effective.java.classesandinterfaces.compositionoverinheritance;
2+
3+
import java.util.Collection;
4+
import java.util.HashSet;
5+
import java.util.List;
6+
7+
// Broken - Inappropriate use of inheritance!
8+
public class InstrumentedHashSet<E> extends HashSet<E> {
9+
// The number of attempted element insertions
10+
private int addCount = 0;
11+
12+
public InstrumentedHashSet() {
13+
}
14+
15+
public InstrumentedHashSet(int initCap, float loadFactor) {
16+
super(initCap, loadFactor);
17+
}
18+
19+
@Override
20+
public boolean add(E e) {
21+
addCount++;
22+
return super.add(e);
23+
}
24+
25+
/*
26+
We don't know that addAll() calls all() under the hood,
27+
the contract might change over time and our class will be broken.
28+
So such inheritance is broken.
29+
Even if API is documented well, still no trust.
30+
*/
31+
@Override
32+
public boolean addAll(Collection<? extends E> c) {
33+
addCount += c.size();
34+
return super.addAll(c);
35+
}
36+
37+
public int getAddCount() {
38+
return addCount;
39+
}
40+
41+
public static void main(String[] args) {
42+
InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
43+
s.addAll(List.of("Snap", "Crackle", "Pop"));
44+
System.out.println(s.getAddCount());
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package by.morka.effective.java.classesandinterfaces.compositionoverinheritance;
2+
3+
import java.util.*;
4+
5+
/*
6+
Decorator pattern in action!
7+
*/
8+
// Wrapper class - uses composition in place of inheritance
9+
/*
10+
Now api is not broken
11+
*/
12+
public class InstrumentedSet<E> extends ForwardingSet<E> {
13+
private int addCount = 0;
14+
15+
public InstrumentedSet(Set<E> s) {
16+
super(s);
17+
}
18+
19+
@Override public boolean add(E e) {
20+
addCount++;
21+
return super.add(e);
22+
}
23+
@Override public boolean addAll(Collection<? extends E> c) {
24+
addCount += c.size();
25+
return super.addAll(c);
26+
}
27+
public int getAddCount() {
28+
return addCount;
29+
}
30+
31+
public static void main(String[] args) {
32+
InstrumentedSet<String> s = new InstrumentedSet<>(new HashSet<>());
33+
s.addAll(List.of("Snap", "Crackle", "Pop"));
34+
System.out.println(s.getAddCount());
35+
}
36+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package by.morka.effective.java.classesandinterfaces.documentinginheritance;
2+
3+
import java.time.Instant;
4+
5+
// Demonstration of what can go wrong when you override a method called from constructor (Page 96)
6+
public final class Sub extends Super {
7+
// Blank final, set by constructor
8+
private final Instant instant;
9+
10+
Sub() {
11+
instant = Instant.now();
12+
}
13+
14+
// Overriding method invoked by superclass constructor
15+
@Override public void overrideMe() {
16+
System.out.println(instant);
17+
}
18+
19+
public static void main(String[] args) {
20+
Sub sub = new Sub();
21+
sub.overrideMe();
22+
}
23+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package by.morka.effective.java.classesandinterfaces.documentinginheritance;
2+
3+
// Class whose constructor invokes an overridable method. NEVER DO THIS! (Page 95)
4+
public class Super {
5+
// Broken - constructor invokes an overridable method
6+
public Super() {
7+
overrideMe();
8+
}
9+
10+
public void overrideMe() {
11+
}
12+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package by.morka.effective.java.classesandinterfaces.immutableclass;
2+
3+
import java.math.BigInteger;
4+
5+
/*
6+
It is final actually because it has no public constructors
7+
Moreover its static factory can return subclasses (PACKAGE PRIVATE ONES, which is great, the client won't know)
8+
*/
9+
public class AnotherComplex {
10+
/*
11+
1. Biginteger is not final, thus can be extended, which is dangerous
12+
2. If your safety depends on Biginteger received from not trustable client, use such algo
13+
*/
14+
public static BigInteger safeInstance(BigInteger bigInteger) {
15+
if (bigInteger.getClass() == BigInteger.class) {
16+
// safe, real biginteger
17+
return bigInteger;
18+
}
19+
// subclass, we need protected copy
20+
return new BigInteger(bigInteger.toByteArray());
21+
}
22+
23+
private final double re;
24+
private final double im;
25+
26+
public static final AnotherComplex ZERO = new AnotherComplex(0, 0);
27+
public static final AnotherComplex ONE = new AnotherComplex(1, 0);
28+
public static final AnotherComplex I = new AnotherComplex(0, 1);
29+
30+
// Private
31+
private AnotherComplex(double re, double im) {
32+
this.re = re;
33+
this.im = im;
34+
}
35+
36+
public double realPart() {
37+
return re;
38+
}
39+
40+
public double imaginaryPart() {
41+
return im;
42+
}
43+
44+
public AnotherComplex plus(AnotherComplex c) {
45+
return new AnotherComplex(re + c.re, im + c.im);
46+
}
47+
48+
// Static factory, used in conjunction with private constructor
49+
// Caching, etc. to minimize instantiation
50+
public static AnotherComplex valueOf(double re, double im) {
51+
return new AnotherComplex(re, im);
52+
}
53+
54+
public AnotherComplex minus(AnotherComplex c) {
55+
return new AnotherComplex(re - c.re, im - c.im);
56+
}
57+
58+
public AnotherComplex times(AnotherComplex c) {
59+
return new AnotherComplex(re * c.re - im * c.im,
60+
re * c.im + im * c.re);
61+
}
62+
63+
public AnotherComplex dividedBy(AnotherComplex c) {
64+
double tmp = c.re * c.re + c.im * c.im;
65+
return new AnotherComplex((re * c.re + im * c.im) / tmp,
66+
(im * c.re - re * c.im) / tmp);
67+
}
68+
69+
@Override
70+
public boolean equals(Object o) {
71+
if (o == this)
72+
return true;
73+
if (!(o instanceof AnotherComplex))
74+
return false;
75+
AnotherComplex c = (AnotherComplex) o;
76+
77+
return Double.compare(c.re, re) == 0
78+
&& Double.compare(c.im, im) == 0;
79+
}
80+
81+
@Override
82+
public int hashCode() {
83+
return 31 * Double.hashCode(re) + Double.hashCode(im);
84+
}
85+
86+
@Override
87+
public String toString() {
88+
return "(" + re + " + " + im + "i)";
89+
}
90+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package by.morka.effective.java.classesandinterfaces.immutableclass;
2+
3+
public final class Complex {
4+
private final double re;
5+
private final double im;
6+
7+
// Expose frequently used instances to minimize instantiation
8+
public static final Complex ZERO = new Complex(0, 0);
9+
public static final Complex ONE = new Complex(1, 0);
10+
public static final Complex I = new Complex(0, 1);
11+
12+
public Complex(double re, double im) {
13+
this.re = re;
14+
this.im = im;
15+
}
16+
17+
public double realPart() {
18+
return re;
19+
}
20+
21+
public double imaginaryPart() {
22+
return im;
23+
}
24+
25+
public Complex plus(Complex c) {
26+
return new Complex(re + c.re, im + c.im);
27+
}
28+
29+
// Static factory, used in conjunction with private constructor
30+
// Caching, etc. to minimize instantiation
31+
public static Complex valueOf(double re, double im) {
32+
return new Complex(re, im);
33+
}
34+
35+
public Complex minus(Complex c) {
36+
return new Complex(re - c.re, im - c.im);
37+
}
38+
39+
public Complex times(Complex c) {
40+
return new Complex(re * c.re - im * c.im,
41+
re * c.im + im * c.re);
42+
}
43+
44+
public Complex dividedBy(Complex c) {
45+
double tmp = c.re * c.re + c.im * c.im;
46+
return new Complex((re * c.re + im * c.im) / tmp,
47+
(im * c.re - re * c.im) / tmp);
48+
}
49+
50+
@Override
51+
public boolean equals(Object o) {
52+
if (o == this)
53+
return true;
54+
if (!(o instanceof Complex))
55+
return false;
56+
Complex c = (Complex) o;
57+
58+
return Double.compare(c.re, re) == 0
59+
&& Double.compare(c.im, im) == 0;
60+
}
61+
62+
@Override
63+
public int hashCode() {
64+
return 31 * Double.hashCode(re) + Double.hashCode(im);
65+
}
66+
67+
@Override
68+
public String toString() {
69+
return "(" + re + " + " + im + "i)";
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package by.morka.effective.java.classesandinterfaces.interfacesoverabstractclasses;
2+
3+
import java.util.Map;
4+
import java.util.Objects;
5+
6+
/*
7+
Skeletal implementation class is nice application for abstract classes
8+
1. define skeleton operations
9+
2. build functionality on skeleton operations
10+
3. inherit it and make work easier
11+
P.S at the same time you can implement interface
12+
*/
13+
public abstract class AbstractMapEntry<K, V> implements Map.Entry<K, V> {
14+
15+
// Entries in a modifiable map must override this method
16+
@Override
17+
public V setValue(V value) {
18+
throw new UnsupportedOperationException();
19+
}
20+
21+
/*
22+
Implements the general contract of Map.Entry.equals
23+
We are not allowed to define default object methods impls in interfaces, so we do it here
24+
*/
25+
@Override
26+
public boolean equals(Object o) {
27+
if (o == this)
28+
return true;
29+
if (!(o instanceof Map.Entry))
30+
return false;
31+
Map.Entry<?, ?> e = (Map.Entry) o;
32+
return Objects.equals(e.getKey(), getKey())
33+
&& Objects.equals(e.getValue(), getValue());
34+
}
35+
36+
37+
/*
38+
Implements the general contract of Map.Entry.hashCode
39+
We are not allowed to define default object methods impls in interfaces, so we do it here
40+
*/
41+
@Override
42+
public int hashCode() {
43+
return Objects.hashCode(getKey())
44+
^ Objects.hashCode(getValue());
45+
}
46+
47+
@Override
48+
public String toString() {
49+
return getKey() + "=" + getValue();
50+
}
51+
}

0 commit comments

Comments
 (0)