“Effective Java (3rd Edition)” நூலிலிருந்து Best Practices

Joshua Bloch எழுதிய “Effective Java (3rd Edition) ” நூலிலிருந்து சில பகுதிகள்

1. null அல்ல, காலியான collection அல்லது array-ஐ return செய்யுங்கள்

Method ஒரு Set-ஐ return செய்தால், காலியான முடிவுக்கு return null; வேண்டாம் – அதற்குப் பதிலாக return Set.of();.

2. Constructor-களுக்குப் பதிலாக static factory method-களைக் கருத்தில் கொள்ளுங்கள்

public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

எப்போதும் சிறந்தது அல்ல, ஆனால் சில பயன்பாடுகளில் இவை பல நன்மைகளைத் தருகின்றன:

குறைபாடுகள்:

3. Constructor parameter-கள் அதிகமாக இருக்கும்போது builder-ஐக் கருத்தில் கொள்ளுங்கள்

Static factory-களுக்கும் constructor-களுக்கும் ஒரு பொதுவான கட்டுப்பாடு உண்டு: அதிக எண்ணிக்கையிலான optional parameter-களுக்கு அவை நன்றாக ஈடுகொடுப்பதில்லை. நான் பணியாற்றிய சில project-களை இது மிகக் கடுமையாகப் பாதித்தது.

4. Private constructor அல்லது enum type மூலம் singleton பண்பை உறுதிசெய்யுங்கள்

பல instance-கள் தேவைப்படாத எதுவும் ஒரே ஒருமுறை மட்டுமே instantiate செய்யப்பட வேண்டும் – @ApplicationScoped மூலமாகவோ வேறொரு singleton pattern மூலமாகவோ.

5. Private constructor மூலம் instantiation-ஐத் தடுங்கள்

சில நேரங்களில், static method-களும் static field-களும் மட்டுமே கொண்ட ஒரு class-ஐ எழுத விரும்புவீர்கள். Object-களின் அடிப்படையில் சிந்திப்பதைத் தவிர்க்க சிலர் இவற்றைத் தவறாகப் பயன்படுத்துவதால் இத்தகைய class-களுக்குக் கெட்ட பெயர் உண்டு; ஆனால் இவற்றுக்கு நியாயமான பயன்பாடுகளும் உள்ளன.

private MyStaticClass() {
    // private constructor to prevent instantiation
}

6. தேவையற்ற object-களை உருவாக்குவதைத் தவிர்த்திடுங்கள்

String s = new String("bikini");  // DON'T DO THIS!

அல்லது:

// Hideously slow! Can you spot the object creation?
private static long sum() {
    Long sum = 0L;
    for (long i = 0; i <= Integer.MAX_VALUE; i++)
        sum += i;
    return sum;
}

தீர்வு: இந்த program சரியான பதிலைத் தருகிறது, ஆனால் ஒரே ஓர் எழுத்துப் பிழையின் காரணமாக இருக்க வேண்டியதை விட மிகவும் மெதுவாக இயங்குகிறது. sum என்ற variable, long-க்குப் பதிலாக Long என அறிவிக்கப்பட்டுள்ளது; இதனால் program சுமார் 2^31 தேவையற்ற Long instance-களை உருவாக்குகிறது (தோராயமாக, long i ஒவ்வொரு முறை Long sum-உடன் கூட்டப்படும்போதும் ஒன்று).

7. Finalizer-களையும் cleaner-களையும் தவிர்த்திடுங்கள்

Finalizer-கள் கணிக்க முடியாதவை, பெரும்பாலும் ஆபத்தானவை, பொதுவாகத் தேவையற்றவை. அவற்றைப் பயன்படுத்துவது ஒழுங்கற்ற நடத்தை, மோசமான performance, portability சிக்கல்கள் ஆகியவற்றை உண்டாக்கலாம்.

8. try-finally-ஐ விட try-with-resources-ஐத் தேர்ந்தெடுங்கள்

// try-with-resources - the best way to close resources!
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
}

9. Comparable-ஐ implement செய்வதைக் கருத்தில் கொள்ளுங்கள்

Comparable-ஐ implement செய்வதன் மூலம், இந்த interface-ஐச் சார்ந்திருக்கும் ஏராளமான generic algorithm-களுடனும் collection implementation-களுடனும் உங்கள் class இணைந்து செயல்பட முடிகிறது.

10. Class-கள், member-களின் அணுகலை (accessibility) முடிந்தவரை குறையுங்கள்

நன்கு வடிவமைக்கப்பட்ட ஒரு component-ஐ மோசமாக வடிவமைக்கப்பட்ட ஒன்றிலிருந்து வேறுபடுத்தும் மிக முக்கியமான காரணி, அந்த component தன் உள் data-வையும் பிற implementation விவரங்களையும் பிற component-களிடமிருந்து எந்த அளவுக்கு மறைக்கிறது என்பதே. நன்கு வடிவமைக்கப்பட்ட component தன் implementation விவரங்கள் அனைத்தையும் மறைத்து, தன் API-ஐ implementation-இலிருந்து தெளிவாகப் பிரிக்கிறது. அதன்பின் component-கள் தங்கள் API-கள் வழியாக மட்டுமே தொடர்புகொள்கின்றன; ஒன்றின் உள் செயல்பாடுகள் பற்றி மற்றொன்றுக்கு எதுவும் தெரியாது. Information hiding அல்லது encapsulation எனப்படும் இந்தக் கருத்து, மென்பொருள் வடிவமைப்பின் அடிப்படைக் கோட்பாடுகளில் ஒன்று.

11. Public class-களில், public field-களை அல்ல, accessor method-களைப் பயன்படுத்துங்கள்

சில நேரங்களில், instance field-களைக் குழுவாக்குவதைத் தவிர வேறு எந்த நோக்கமும் இல்லாத, சீரழிந்த (degenerate) class-களை எழுத ஆசை வரலாம்:

// Degenerate classes like this should not be public!
class Point {
    public double x;
    public double y;
}

இத்தகைய class-களின் data field-கள் நேரடியாக அணுகப்படுவதால், இவை encapsulation-இன் நன்மைகளைத் தருவதில்லை. API-ஐ மாற்றாமல் representation-ஐ மாற்ற முடியாது, invariant-களை உறுதிசெய்ய முடியாது, ஒரு field அணுகப்படும்போது கூடுதல் நடவடிக்கை எதுவும் எடுக்கவும் முடியாது. கடும்போக்கு object-oriented programmer-கள், இத்தகைய class-கள் முற்றிலும் வெறுக்கத்தக்கவை என்றும், அவற்றை எப்போதும் private field-களும் public accessor method-களும் (getter-கள்), mutable class-களுக்கு mutator-களும் (setter-கள்) கொண்ட class-களால் மாற்ற வேண்டும் என்றும் கருதுகிறார்கள்:

// Encapsulation of data by accessor methods and mutators
class Point {

    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() { return x; }
    public double getY() { return y; }

    public void setX(double x) { this.x = x; }
    public void setY(double y) { this.y = y; }
}

Public class-களைப் பொறுத்தவரை கடும்போக்காளர்கள் சொல்வது நிச்சயமாகச் சரி: ஒரு class அதன் package-க்கு வெளியே அணுகக்கூடியதாக இருந்தால், class-இன் உள் representation-ஐ மாற்றும் நெகிழ்வுத்தன்மையைத் தக்கவைக்க accessor method-களை வழங்குங்கள். ஒரு public class தன் data field-களை வெளிப்படுத்தினால், client code எங்கெங்கும் பரவியிருக்கக்கூடும் என்பதால், அதன் representation-ஐ மாற்றும் நம்பிக்கை முழுவதும் போய்விடும்.

ஆனால் ஒரு class package-private ஆகவோ private nested class ஆகவோ இருந்தால், அதன் data field-களை வெளிப்படுத்துவதில் அடிப்படையில் தவறு எதுவும் இல்லை – அந்த field-கள் class வழங்கும் abstraction-ஐப் போதுமான அளவு விவரிக்கும் பட்சத்தில்.

12. Mutability-ஐ முடிந்தவரை குறையுங்கள்

Immutable class என்பது, அதன் instance-களை மாற்ற முடியாத ஒரு class. ஒவ்வொரு instance-இலும் உள்ள தகவல் அனைத்தும் object-இன் வாழ்நாள் முழுவதும் நிலையாக இருக்கும்; எனவே எந்த மாற்றத்தையும் ஒருபோதும் காண முடியாது. Java platform library-களில் பல immutable class-கள் உள்ளன: String, boxed primitive class-கள், BigInteger, BigDecimal போன்றவை. இதற்குப் பல நல்ல காரணங்கள் உண்டு: immutable class-களை mutable class-களை விட வடிவமைப்பதும், implement செய்வதும், பயன்படுத்துவதும் எளிது. அவற்றில் பிழைகளுக்கு வாய்ப்பு குறைவு, அவை அதிகப் பாதுகாப்பானவை. ஒரு class-ஐ immutable ஆக்க, இந்த ஐந்து விதிகளைப் பின்பற்றுங்கள்:

  1. Object-இன் state-ஐ மாற்றும் method-களை வழங்காதீர்கள் (இவை mutator-கள் எனப்படும்).
  2. Class-ஐ extend செய்ய முடியாதபடி உறுதிசெய்யுங்கள். Object-இன் state மாறிவிட்டதுபோல் நடந்துகொண்டு, கவனக்குறைவான அல்லது தீங்கிழைக்கும் subclass-கள் class-இன் immutable நடத்தையைக் கெடுப்பதை இது தடுக்கிறது. Subclass உருவாக்கத்தைத் தடுப்பது பொதுவாக, எடுத்துக்காட்டாக, class-ஐ final ஆக்குவதன் மூலம் செய்யப்படுகிறது.
  3. எல்லா field-களையும் final ஆக்குங்கள். System தானே அமல்படுத்தும் வகையில் இது உங்கள் நோக்கத்தைத் தெளிவாக வெளிப்படுத்துகிறது. மேலும், memory model-இல் விவரிக்கப்பட்டுள்ளபடி, புதிதாக உருவாக்கப்பட்ட ஒரு instance-இன் reference, synchronization இல்லாமல் ஒரு thread-இலிருந்து இன்னொன்றுக்கு அனுப்பப்பட்டால் சரியான நடத்தையை உறுதிசெய்யவும் இது அவசியம்.
  4. எல்லா field-களையும் private ஆக்குங்கள். Field-கள் குறிப்பிடும் mutable object-களை client-கள் அணுகி நேரடியாக மாற்றுவதை இது தடுக்கிறது. Primitive மதிப்புகளையோ immutable object-களுக்கான reference-களையோ கொண்ட public final field-களை immutable class-கள் வைத்திருப்பது தொழில்நுட்ப ரீதியாக அனுமதிக்கப்பட்டாலும், அது பரிந்துரைக்கப்படுவதில்லை; ஏனெனில் பிந்தைய release-இல் உள் representation-ஐ மாற்றுவதை அது தடுத்துவிடுகிறது.
  5. எந்த mutable component-க்கும் பிரத்யேக அணுகலை உறுதிசெய்யுங்கள். உங்கள் class-இல் mutable object-களைக் குறிப்பிடும் field-கள் இருந்தால், class-இன் client-கள் அந்த object-களுக்கான reference-களைப் பெற முடியாதபடி உறுதிசெய்யுங்கள். அத்தகைய field-ஐ client வழங்கிய object reference கொண்டு ஒருபோதும் initialize செய்யாதீர்கள்; accessor-இலிருந்து அந்த field-ஐ return செய்யவும் வேண்டாம். Constructor-கள், accessor-கள், readObject method-கள் ஆகியவற்றில் defensive copy-களை உருவாக்குங்கள்.

13. Inheritance-ஐ விட composition-க்கு முன்னுரிமை கொடுங்கள்

Code-ஐ மறுபயன்பாடு செய்ய inheritance ஒரு வலிமையான வழி, ஆனால் அது எப்போதும் அந்த வேலைக்குச் சிறந்த கருவி அல்ல. தவறாகப் பயன்படுத்தினால், அது எளிதில் உடையக்கூடிய மென்பொருளை உருவாக்குகிறது. Subclass, superclass implementation-கள் ஒரே programmer-களின் கட்டுப்பாட்டில் இருக்கும் ஒரு package-க்குள் inheritance-ஐப் பயன்படுத்துவது பாதுகாப்பானது. Extend செய்வதற்காகவே வடிவமைக்கப்பட்டு ஆவணப்படுத்தப்பட்ட class-களை extend செய்யும்போதும் inheritance பாதுகாப்பானது. ஆனால் package எல்லைகளைக் கடந்து சாதாரண concrete class-களிலிருந்து inherit செய்வது ஆபத்தானது.

Method அழைப்பைப் போலல்லாமல், inheritance encapsulation-ஐ மீறுகிறது. புதிய method-களை மட்டும் சேர்த்து, ஏற்கெனவே உள்ள method-களை override செய்யாமல் இருந்தால், ஒரு class-ஐ extend செய்வது பாதுகாப்பானது என்று நீங்கள் நினைக்கலாம். இத்தகைய extension மிகவும் பாதுகாப்பானது என்றாலும், அபாயமற்றது அல்ல. அடுத்த release-இல் superclass-க்கு ஒரு புதிய method கிடைத்து, துரதிர்ஷ்டவசமாக நீங்கள் subclass-இல் அதே signature-உம் வேறு return type-உம் கொண்ட ஒரு method-ஐ வைத்திருந்தால், உங்கள் subclass இனி compile ஆகாது. புதிய superclass method-இன் அதே signature-உம் return type-உம் கொண்ட method-ஐ subclass-இல் வைத்திருந்தால், இப்போது நீங்கள் அதை override செய்கிறீர்கள்; எனவே முன்பு விவரித்த சிக்கல்களுக்கு ஆளாவீர்கள். மேலும், உங்கள் method புதிய superclass method-இன் contract-ஐ நிறைவேற்றுமா என்பது சந்தேகமே; ஏனெனில் நீங்கள் subclass method-ஐ எழுதியபோது அந்த contract இன்னும் எழுதப்படவில்லை.

அதிர்ஷ்டவசமாக, மேலே விவரித்த எல்லாச் சிக்கல்களையும் தவிர்க்க ஒரு வழி உண்டு. ஏற்கெனவே உள்ள class-ஐ extend செய்வதற்குப் பதிலாக, அந்த class-இன் ஒரு instance-ஐக் குறிப்பிடும் ஒரு private field-ஐ உங்கள் புதிய class-க்குக் கொடுங்கள். இருக்கும் class புதிய class-இன் ஒரு component ஆகிவிடுவதால், இந்த வடிவமைப்பு composition எனப்படுகிறது. புதிய class-இன் ஒவ்வொரு instance method-உம், உள்ளே இருக்கும் பழைய class instance-இன் தொடர்புடைய method-ஐ அழைத்து, முடிவுகளை return செய்கிறது. இது forwarding எனப்படுகிறது; புதிய class-இன் method-கள் forwarding method-கள் எனப்படுகின்றன. இதன் விளைவாகக் கிடைக்கும் class பாறைபோல் உறுதியானது; பழைய class-இன் implementation விவரங்களை அது சார்ந்திருப்பதில்லை. பழைய class-இல் புதிய method-களைச் சேர்த்தாலும்கூட புதிய class-இல் எந்தத் தாக்கமும் இருக்காது.

14. Abstract class-களை விட interface-களுக்கு முன்னுரிமை கொடுங்கள்

பல implementation-களை அனுமதிக்கும் ஒரு type-ஐ வரையறுக்க Java-வில் இரண்டு வழிமுறைகள் உள்ளன: interface-களும் abstract class-களும். Java 8-இல் interface-களுக்கு default method-கள் அறிமுகமானதிலிருந்து, இரண்டு வழிமுறைகளிலும் சில instance method-களுக்கு implementation வழங்க முடியும்.

ஒரு முக்கிய வேறுபாடு: abstract class வரையறுக்கும் type-ஐ implement செய்ய, ஒரு class அந்த abstract class-இன் subclass ஆக இருக்க வேண்டும். Java ஒற்றை inheritance-ஐ மட்டுமே அனுமதிப்பதால், abstract class-களின் இந்தக் கட்டுப்பாடு type வரையறைகளாக அவற்றின் பயன்பாட்டைக் கடுமையாக வரம்புக்குள்ளாக்குகிறது. தேவையான எல்லா method-களையும் வரையறுத்து general contract-ஐப் பின்பற்றும் எந்த class-உம், class hierarchy-இல் எங்கிருந்தாலும், ஒரு interface-ஐ implement செய்யலாம். ஏற்கெனவே உள்ள class-களில் ஒரு புதிய interface-ஐ எளிதாகப் பொருத்தி implement செய்யலாம்.

15. Generics: raw type-களைப் பயன்படுத்தாதீர்கள்

பிழை நடந்து நீண்ட நேரம் கழித்து, runtime-இல்தான் அதைக் கண்டுபிடிப்பீர்கள் – அதுவும் பிழை உள்ள code-இலிருந்து வெகு தொலைவில் இருக்கக்கூடிய code-இல். ClassCastException-ஐப் பார்த்தவுடன், stamp சேகரிப்புக்குள் நாணயத்தைப் போட்ட அந்த method அழைப்பைத் தேடி codebase முழுவதையும் அலச வேண்டியிருக்கும். Compiler-ஆல் உங்களுக்கு உதவ முடியாது.

16. Generics: unchecked warning-களை நீக்குங்கள்

Generics கொண்டு program எழுதும்போது, பல compiler warning-களைக் காண்பீர்கள்:

Generics-இல் உங்கள் அனுபவம் கூடக்கூட warning-கள் குறையும்; ஆனால் புதிதாக எழுதிய code சுத்தமாக compile ஆகும் என்று எதிர்பார்க்காதீர்கள். பல unchecked warning-களை எளிதாக நீக்கலாம். எடுத்துக்காட்டாக, தவறுதலாக இந்த declaration-ஐ எழுதிவிட்டீர்கள் என்று வைத்துக்கொள்ளுங்கள்:

Set<Lark> exaltation = new HashSet();

இதை இப்படி மாற்றுங்கள்:

Set<Lark> exaltation = new HashSet<>();

சில warning-களை நீக்குவது மிகவும் கடினமாக இருக்கும். கொஞ்சம் யோசிக்க வேண்டிய warning-கள் வரும்போது, விடாமுயற்சியுடன் இருங்கள்! உங்களால் முடிந்த ஒவ்வொரு unchecked warning-ஐயும் நீக்குங்கள்.

எல்லா warning-களையும் நீக்கினால், உங்கள் code typesafe என்பது உறுதி – இது மிக நல்ல விஷயம். Runtime-இல் ClassCastException வராது என்பது இதன் பொருள்; உங்கள் program நீங்கள் நினைத்தபடியே நடந்துகொள்ளும் என்ற நம்பிக்கையையும் இது அதிகரிக்கிறது. ஒரு warning-ஐ நீக்க முடியாவிட்டாலும், அதைத் தூண்டிய code typesafe என்று நிரூபிக்க முடிந்தால், அப்போது (அப்போது மட்டுமே) @SuppressWarnings("unchecked") annotation மூலம் அந்த warning-ஐ அடக்குங்கள். Code typesafe என்று முதலில் நிரூபிக்காமல் warning-களை அடக்கினால், உங்களுக்கு நீங்களே ஒரு போலியான பாதுகாப்பு உணர்வைத் தருகிறீர்கள். Code எந்த warning-உம் இல்லாமல் compile ஆகலாம், ஆனாலும் runtime-இல் ClassCastException-ஐ வீசலாம்.

ஒவ்வொரு முறை @SuppressWarnings("unchecked") annotation-ஐப் பயன்படுத்தும்போதும், அது ஏன் பாதுகாப்பானது என்று ஒரு comment சேருங்கள்.

17. Generics: எப்போது அர்த்தமுள்ளது

ஐயோ – generics மிகவும் தேவைப்படும் ஒரு class:

Generics தேவைப்படும் ஒரு class-இன் எடுத்துக்காட்டு

இது போன்ற ஒன்றை, மிகத் தாமதமாக என்றாலும், merge request-இலேயே பிடித்துவிட வேண்டும்!

18. Generics: இப்படிச் செய்யக் கூடாது

மன்னிக்கவும், வேறு எதுவும் சொல்லத் தேவையில்லை. தயவுசெய்து இதை ஒருபோதும் merge செய்யாதீர்கள்:

public abstract class JpaLearningCardBaseContentService<LC extends LearningCardBaseContent<L, LC, AC>, J extends JpaLearningCardBaseContent<L, LC, AC>, L extends
        LearningCardBase<LC, A>, A extends AnswerOptionBase<L, AC>, AC extends AnswerOptionBaseContent<LC, AC, A>, AS extends AnswerOptionBaseService<A, AC, L, LC>, ACS extends
        AnswerOptionBaseContentService<AC, A, L, LC>, LS extends LearningCardBaseService<L>>
    extends JpaLightPersistenceService<LC, J, Long> implements LearningCardBaseContentService<LC, L, A, AC> {
    // ...
}

ஒரு class declaration-ஐப் புரிந்துகொள்ள மட்டுமே 5 நிமிடங்கள் ஒருபோதும் ஆகக் கூடாது.

19. Array-களை விட list-களுக்கு முன்னுரிமை கொடுங்கள்

Array-கள் இரண்டு முக்கிய வழிகளில் generic type-களிலிருந்து வேறுபடுகின்றன. முதலாவதாக, array-கள் covariant. பயமுறுத்தும் இந்தச் சொல்லின் பொருள் எளிமையானது: Sub என்பது Super-இன் subtype என்றால், array type Sub[], array type Super[]-இன் subtype. மாறாக, generics invariant: எந்த இரண்டு வெவ்வேறு type-கள் Type1, Type2-க்கும், List<Type1> என்பது List<Type2>-இன் subtype-உம் அல்ல, supertype-உம் அல்ல.

20. Anonymous class-களை விட lambda-க்களுக்கு முன்னுரிமை கொடுங்கள்

// Anonymous class instance as a function object - obsolete!
Collections.sort(words, new Comparator<String>() {
    public int compare(String s1, String s2) {
        return Integer.compare(s1.length(), s2.length());
    }
});

அதற்குப் பதிலாக, இப்படிச் செய்யுங்கள்:

// Lambda expression as function object (replaces anonymous class)
Collections.sort(words, (s1, s2) -> Integer.compare(s1.length(), s2.length()));

21. Local variable-களின் scope-ஐ முடிந்தவரை குறையுங்கள்

ஒரு local variable-இன் scope-ஐக் குறைப்பதற்கான மிக வலிமையான உத்தி, அது முதலில் பயன்படுத்தப்படும் இடத்திலேயே அதை அறிவிப்பது...

22. பாரம்பரிய for loop-களை விட for-each loop-களுக்கு முன்னுரிமை கொடுங்கள்

// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
    ... // Do something with e
}

23. Library-களை அறிந்து பயன்படுத்துங்கள்

பூஜ்ஜியத்துக்கும் ஒரு மேல் வரம்புக்கும் இடையே random integer-களை உருவாக்க விரும்புகிறீர்கள் என்று வைத்துக்கொள்ளுங்கள். இந்தப் பொதுவான பணியை எதிர்கொள்ளும் பல programmer-கள் கிட்டத்தட்ட இப்படி ஒரு சிறிய method-ஐ எழுதுவார்கள்:

// Common but deeply flawed!
static Random rnd = new Random();

static int random(int n) {
    return Math.abs(rnd.nextInt()) % n;
}

இந்த method நன்றாகத் தோன்றலாம், ஆனால் அதில் மூன்று குறைபாடுகள் உள்ளன. முதலாவது: n இரண்டின் ஒரு சிறிய அடுக்காக (power of two) இருந்தால், random எண்களின் வரிசை சற்றுக் குறுகிய காலத்திலேயே மீண்டும் வரத் தொடங்கும். இரண்டாவது குறைபாடு: n இரண்டின் அடுக்காக இல்லாவிட்டால், சராசரியாக சில எண்கள் மற்றவற்றை விட அடிக்கடி return செய்யப்படும். n பெரிதாக இருந்தால், இந்த விளைவு மிகத் தெளிவாகத் தெரியலாம்.

random method-இன் மூன்றாவது குறைபாடு: அரிதான சந்தர்ப்பங்களில் அது பேரழிவுகரமாகத் தோல்வியடைந்து, குறிப்பிட்ட வரம்புக்கு வெளியே உள்ள ஓர் எண்ணை return செய்யலாம். ஏனெனில், rnd.nextInt() return செய்யும் மதிப்பை, Math.abs-ஐ அழைத்து எதிர்மறையல்லாத ஒரு int-ஆக மாற்ற method முயல்கிறது. nextInt() Integer.MIN_VALUE-ஐ return செய்தால், Math.abs-உம் Integer.MIN_VALUE-ஐயே return செய்யும்; n இரண்டின் அடுக்காக இல்லை என்றால், மீதி operator (%) ஓர் எதிர்மறை எண்ணை return செய்யும். இது கிட்டத்தட்ட நிச்சயமாக உங்கள் program-ஐத் தோல்வியடையச் செய்யும்; அந்தத் தோல்வியை மீண்டும் உருவாக்குவதும் கடினமாக இருக்கலாம்.

24. துல்லியமான பதில்கள் தேவைப்படும்போது float, double-ஐத் தவிர்த்திடுங்கள்

பணத் தொகைகளுக்கு BigDecimal-ஐப் பயன்படுத்துங்கள். float, double ஆகியவை rounding பிழைகளை உண்டாக்குகின்றன.

எடுத்துக்காட்டாக, உங்கள் பையில் $1.03 இருக்கிறது, நீங்கள் 42¢ செலவழிக்கிறீர்கள் என்று வைத்துக்கொள்ளுங்கள். உங்களிடம் எவ்வளவு பணம் மீதமிருக்கும்? இந்தக் கேள்விக்குப் பதிலளிக்க முயலும் ஓர் அப்பாவியான program துண்டு இதோ:

System.out.println(1.03 - 0.42);

துரதிர்ஷ்டவசமாக, இது 0.6100000000000001 என்று print செய்கிறது. இது தனிப்பட்ட ஒரு நிகழ்வு அல்ல. உங்கள் பையில் ஒரு டாலர் இருக்கிறது, ஒவ்வொன்றும் பத்து சென்ட் விலையுள்ள ஒன்பது washer-களை வாங்குகிறீர்கள் என்று வைத்துக்கொள்ளுங்கள். உங்களுக்கு எவ்வளவு மீதிச் சில்லறை கிடைக்கும்?

System.out.println(1.00 - 9 * 0.10);

இந்த program துண்டின்படி, உங்களுக்குக் கிடைப்பது $0.09999999999999998.

Print செய்வதற்கு முன் முடிவுகளை round செய்தாலே சிக்கல் தீர்ந்துவிடும் என்று நீங்கள் நினைக்கலாம்; ஆனால் துரதிர்ஷ்டவசமாக, அது எப்போதும் வேலை செய்யாது. எடுத்துக்காட்டாக, உங்கள் பையில் ஒரு டாலர் இருக்கிறது; ஓர் அலமாரியில் 10¢, 20¢, 30¢ என ஒரு டாலர் வரை விலையுள்ள சுவையான மிட்டாய்கள் வரிசையாக இருக்கின்றன. 10¢ மிட்டாயிலிருந்து தொடங்கி, அலமாரியில் அடுத்த மிட்டாயை வாங்க முடியாத வரை, ஒவ்வொன்றிலும் ஒன்றை வாங்குகிறீர்கள்.

25. Boxed primitive-களை விட primitive type-களுக்கு முன்னுரிமை கொடுங்கள்

// Broken comparator - can you spot the flaw?
Comparator<Integer> naturalOrder = (i, j) -> (i < j) ? -1 : (i == j ? 0 : 1);

26. Object-களை அவற்றின் interface-கள் மூலம் குறிப்பிடுங்கள்

நல்ல எடுத்துக்காட்டு:

Set<Son> sonSet = new LinkedHashSet<>();

மோசமான எடுத்துக்காட்டு:

LinkedHashSet<Son> sonSet = new LinkedHashSet<>();

27. Reflection-ஐ விட interface-களுக்கு முன்னுரிமை கொடுங்கள்

இல்லையென்றால்:

28. பொதுவாக ஏற்றுக்கொள்ளப்பட்ட naming convention-களைப் பின்பற்றுங்கள்

Java platform-க்கு நன்கு நிலைபெற்ற naming convention-களின் தொகுப்பு ஒன்று உள்ளது; அவற்றில் பல The Java Language Specification [JLS, 6.1]-இல் இடம்பெற்றுள்ளன.

29. Exception-களை விதிவிலக்கான சூழல்களுக்கு மட்டுமே பயன்படுத்துங்கள்

// Horrible abuse of exceptions. Don't ever do this!
try {
    int i = 0;
    while (true) {
        range[i++].climb();
    }
} catch (ArrayIndexOutOfBoundsException e) {
    // catch it
}

இந்த code என்ன செய்கிறது? பார்த்தவுடன் அது சிறிதும் தெளிவாகத் தெரியவில்லை; அதைப் பயன்படுத்தாமல் இருக்க அதுவே போதுமான காரணம். உண்மையில், ஒரு array-இன் உறுப்புகள் வழியாக loop செய்வதற்கான, மிக மோசமாகச் சிந்திக்கப்பட்ட idiom இது. Array-இன் எல்லைக்கு வெளியே உள்ள முதல் உறுப்பை அணுக முயலும்போது, ஒரு ArrayIndexOutOfBoundsException-ஐ வீசி, பிடித்து, புறக்கணிப்பதன் மூலம் இந்த முடிவற்ற loop முடிகிறது. Array வழியாக loop செய்வதற்கான standard idiom-க்கு இது சமமானதாக இருக்க வேண்டும்; அந்த idiom-ஐ எந்த Java programmer-உம் உடனே அடையாளம் கண்டுகொள்வார்:

for (Mountain m : range) {
    m.climb();
}

பெயரே சொல்வதுபோல, exception-கள் விதிவிலக்கான சூழல்களுக்கு மட்டுமே; சாதாரண control flow-க்கு அவற்றை ஒருபோதும் பயன்படுத்தக் கூடாது.

30. மீளக்கூடிய சூழல்களுக்கு checked exception-களையும், programming பிழைகளுக்கு runtime exception-களையும் பயன்படுத்துங்கள்

அழைப்பவர் நியாயமாக மீண்டுவர முடியும் என்று எதிர்பார்க்கக்கூடிய சூழல்களுக்கு checked exception-களைப் பயன்படுத்துங்கள். ஒரு checked exception-ஐப் பயனரின் முன் வைப்பதன் மூலம், API வடிவமைப்பாளர் அந்தச் சூழலிலிருந்து மீண்டுவர வேண்டும் என்ற கட்டளையை முன்வைக்கிறார். Exception-ஐப் பிடித்துப் புறக்கணிப்பதன் மூலம் பயனர் அந்தக் கட்டளையை அலட்சியம் செய்யலாம், ஆனால் அது பொதுவாக மோசமான யோசனை.

Programming பிழைகளைக் குறிக்க runtime exception-களைப் பயன்படுத்துங்கள். பெரும்பாலான runtime exception-கள் precondition மீறல்களைக் குறிக்கின்றன. Precondition மீறல் என்பது, API specification நிர்ணயித்த contract-ஐ ஒரு API-இன் client பின்பற்றத் தவறுவதுதான். எடுத்துக்காட்டாக, array அணுகலுக்கான contract, array index பூஜ்ஜியத்துக்கும் array நீளம் கழித்தல் ஒன்றுக்கும் இடையே (இரண்டும் உட்பட) இருக்க வேண்டும் என்று கூறுகிறது. இந்த precondition மீறப்பட்டதை ArrayIndexOutOfBoundsException குறிக்கிறது.

31. Checked exception-களைத் தேவையில்லாமல் பயன்படுத்துவதைத் தவிர்த்திடுங்கள்

சுருக்கமாக, அளவோடு பயன்படுத்தினால் checked exception-கள் program-களின் நம்பகத்தன்மையை அதிகரிக்கலாம்; அளவுக்கு மீறிப் பயன்படுத்தினால், API-களைப் பயன்படுத்துவது வேதனையாகிவிடும்.

Pair<Boolean, LearningCardGroup> answerLearningCard(@NotNull LearningCardGroup learningCardGroup,
                                                      LearningCardAssignment learningCardAssignment,
                                                      @NotNull Collection<AnswerOption> answers)
      throws InvalidTrainingStateException, PersistenceException, LearningCardGroupAlreadyCompletedException,
             ConsecutiveCorrectCountExceededException,
             PreviousLearningCardGroupUncompletedException, LearningCardGroupCooldownException,
             LearningCardGroupHasUncompletedLearningCardsException, InvalidParticipantException, TrainingNotSignedException; // but why ???
     // this is a mistake seen a lot with junior devs
     // exceptions are exceptionally slow and should be avoided for mere validation
     // the performance gets even worse if a stacktrace is printed, since logging and printing is one of the slowest possible actions

32. Standard exception-களைப் பயன்படுத்துவதற்கு முன்னுரிமை கொடுங்கள்

நிபுணத்துவம் வாய்ந்த programmer-களை அனுபவம் குறைந்தவர்களிடமிருந்து வேறுபடுத்தும் ஒரு பண்பு: நிபுணர்கள் அதிக அளவு code மறுபயன்பாட்டுக்கு முயல்கிறார்கள், பொதுவாக அதை அடைகிறார்கள்.

Code மறுபயன்பாடு நல்லது என்ற விதிக்கு exception-களும் விதிவிலக்கல்ல. பெரும்பாலான API-களின் exception வீசும் தேவைகளில் பெரும்பாலானவற்றை ஈடுசெய்யும் exception-களின் தொகுப்பை Java library-கள் வழங்குகின்றன. Standard exception-களை மறுபயன்பாடு செய்வதில் பல நன்மைகள் உள்ளன. அவற்றில் முதன்மையானது: programmer-களுக்கு ஏற்கெனவே பழக்கமான, நிலைபெற்ற மரபுகளுடன் பொருந்துவதால், உங்கள் API-ஐக் கற்பதும் பயன்படுத்துவதும் எளிதாகிறது. அதற்கு அடுத்தபடியாக: உங்கள் API-ஐப் பயன்படுத்தும் program-கள் பழக்கமில்லாத exception-களால் நிரம்பியிருக்காததால், அவற்றைப் படிப்பது எளிது. கடைசியாக (முக்கியத்துவத்திலும் கடைசி), குறைவான exception class-கள் என்றால் குறைவான memory footprint, class-களை load செய்வதில் குறைவான நேரம்.

மிக அதிகமாக மறுபயன்பாடு செய்யப்படும் exception type IllegalArgumentException. பொருத்தமற்ற மதிப்புள்ள argument-ஐ அழைப்பவர் அனுப்பும்போது பொதுவாக வீசப்பட வேண்டிய exception இதுதான். எடுத்துக்காட்டாக, ஒரு செயல் எத்தனை முறை திரும்பச் செய்யப்பட வேண்டும் என்பதைக் குறிக்கும் parameter-இல் அழைப்பவர் ஓர் எதிர்மறை எண்ணை அனுப்பினால், வீச வேண்டியது இந்த exception-ஐத்தான். அடிக்கடி மறுபயன்பாடு செய்யப்படும் இன்னொரு exception IllegalStateException. அழைப்பைப் பெறும் object-இன் state காரணமாக அழைப்பு செல்லாததாக இருக்கும்போது பொதுவாக வீசப்பட வேண்டியது இதுதான். எடுத்துக்காட்டாக, ஒரு object முறையாக initialize ஆவதற்கு முன்பே அழைப்பவர் அதைப் பயன்படுத்த முயன்றால், வீச வேண்டியது இந்த exception-ஐத்தான்.

தவறான ஒவ்வொரு method அழைப்பும் இறுதியில் ஒரு செல்லாத argument அல்லது state-க்கே வந்து சேர்கிறது என்று வாதிடலாம்; ஆனால் சில குறிப்பிட்ட வகை செல்லாத argument-கள், state-களுக்கு வேறு exception-கள் வழக்கமாகப் பயன்படுத்தப்படுகின்றன. null மதிப்புகள் தடைசெய்யப்பட்ட ஒரு parameter-இல் அழைப்பவர் null-ஐ அனுப்பினால், மரபுப்படி IllegalArgumentException-க்குப் பதிலாக NullPointerException வீசப்பட வேண்டும். அதுபோலவே, ஒரு sequence-இன் index-ஐக் குறிக்கும் parameter-இல் அழைப்பவர் வரம்புக்கு வெளியே உள்ள மதிப்பை அனுப்பினால், IllegalArgumentException-க்குப் பதிலாக IndexOutOfBoundsException வீசப்பட வேண்டும்.

Exception, RuntimeException, Throwable, Error ஆகியவற்றை நேரடியாக மறுபயன்பாடு செய்யாதீர்கள். இந்த class-களை abstract போலவே கருதுங்கள். இருந்தாலும், ஒரு checked exception-ஐ runtime exception-ஆக மாற்ற விரும்பினால், நீங்கள் என்ன செய்கிறீர்கள் என்று உண்மையிலேயே தெரிந்திருந்தால், RuntimeException-ஐப் பயன்படுத்தலாம்.

33. Exception-களைப் புறக்கணிக்காதீர்கள்

// Empty catch block ignores exception - Highly suspect!
try {
    ...
} catch (SomeException e) {
}

காலியான catch block, exception-களின் நோக்கத்தையே தோற்கடிக்கிறது; விதிவிலக்கான சூழல்களைக் கையாள உங்களைக் கட்டாயப்படுத்துவதே அந்த நோக்கம். ஒரு exception-ஐப் புறக்கணிப்பது, தீ எச்சரிக்கை மணியைப் புறக்கணிப்பதற்குச் சமம் – அதோடு, உண்மையிலேயே தீப்பிடித்திருக்கிறதா என்று வேறு யாரும் பார்க்க முடியாதபடி அதை அணைத்துவிடுவதற்கும் சமம்.