0% found this document useful (0 votes)
96 views19 pages

Android - How To Improve Code Quality

The document discusses code quality and conventions for Kotlin and Java. It provides links to style guides and outlines naming rules, formatting guidelines, and best practices for Kotlin including using expressions, top-level functions, named arguments, and avoiding null checks.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
96 views19 pages

Android - How To Improve Code Quality

The document discusses code quality and conventions for Kotlin and Java. It provides links to style guides and outlines naming rules, formatting guidelines, and best practices for Kotlin including using expressions, top-level functions, named arguments, and avoiding null checks.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 19

How to improve code

quality
The style guide for Kotlin convention
Link: https://kotlinlang.org/docs/reference/coding-conventions.html

Link: https://developer.android.com/kotlin/style-guide

The style guide for Java convention


Link: https://google.github.io/styleguide/javaguide.html

Link: https://www.oracle.com/technetwork/java/javase/documentation/codeconvtoc-
136057.html

Rule of 30
If an element consists of more than 30 subelements, it is highly probable that there is a
serious problem:
a) Methods should not have more than an average of 30 code lines (not counting line spaces
and comments).
b) A class should contain an average of less than 30 methods, resulting in up to 900 lines of
code.
c) A package shouldn’t contain more than 30 classes, thus comprising up to 27,000 code
lines.
d) Subsystems with more than 30 packages should be avoided. Such a subsystem would
count up to 900 classes with up to 810,000 lines of code.
e) A system with 30 subsystems would thus possess 27,000 classes and 24.3 million code
lines.

1
I. Kotlin convention
To configure the IntelliJ formatter according to this style guide, please install Kotlin plugin version
1.2.20 or newer, go to Settings | Editor | Code Style | Kotlin, click on "Set from…" link in the upper
right corner, and select "Predefined style / Kotlin style guide" from the menu.

1. Naming rules (https://kotlinlang.org/docs/reference/coding-conventions.html#naming-rules)

a. Classes and objects start with an upper case letter and use camel humps:

open class DeclarationProcessor { ... }

object EmptyDeclarationProcessor : DeclarationProcessor() { ... }

b. Functions, properties and local variables start with a lower case letter and use camel humps
and no underscores:

fun processDeclarations() { ... }

var declarationCount = ...

c. Exception: factory functions used to create instances of classes can have the same name as
the class being created:

abstract class Foo { ... }

class FooImpl : Foo { ... }

fun Foo(): Foo { return FooImpl(...) }

d. Test methods

class MyTestCase {
@Test fun `ensure everything works`() { ... }

@Test fun ensureEverythingWorks_onAndroid() { ... }


}

e. Constants, Enum

const val MAX_COUNT = 8

val USER_NAME_FIELD = "UserName"

enum class Color { RED, GREEN }

f. Names of top-level or object properties which hold objects with behavior or mutable data

val mutableCollection: MutableSet<String> = HashSet()

g. Properties holding references to singleton objects

val PersonComparator: Comparator<Person> = ...

h. If a class has two properties which are conceptually the same but one is part of a public API and
another is an implementation detail, use an underscore as the prefix for the name of the private
property:

2
class C {
private val _elementList = mutableListOf<Element>()

val elementList: List<Element>


get() = _elementList
}

2. Formatting (https://kotlinlang.org/docs/reference/coding-conventions.html#formatting)

3. Copyright / License
If a copyright or license header belongs in the file it should be placed at the immediate top in a multi-
line comment.
/*
* Copyright 2017 Google, Inc.
*
* ...
*/
Do not use a KDoc-style or single-line-style comment.
/**
* Copyright 2017 Google, Inc.
*
* ...
*/

// Copyright 2017 Google, Inc.


//
// …

4. Expressions

val value = if (string.isEmpty()) 0 else 1 // Okay

val value = if (string.isEmpty()) // WRONG!


0
else
1

val value = if (string.isEmpty()) { // Okay


0
} else {
1
}

try {
doSomething()
} catch (e: Exception) {} // WRONG!

try {
doSomething()
} catch (e: Exception) {
e.printStackTrace();
} // Okay

3
5. Horizontal whitespace

// WRONG!
for(i in 0..1) {
}

// Okay
for (i in 0..1) {
}

// WRONG!
}else {
}
// Okay
} else {
}

// WRONG!
if (list.isEmpty()){
}

// Okay
if (list.isEmpty()) {
}

// Okay
package com.example.deepspace
// WRONG!
package com.example.deepSpace
// WRONG!
package com.example.deep_space

6. Braces

if (string.isEmpty())
return // WRONG!

if (string.isEmpty()) {
return // Okay
}

II. Best Practices (https://phauer.com/2017/idiomatic-kotlin-best-practices/)


1. Use Expressions

// Don't
fun getDefaultLocale(deliveryArea: String): Locale {
val deliverAreaLower = deliveryArea.toLowerCase()
if (deliverAreaLower == "germany" || deliverAreaLower == "austria") {
return Locale.GERMAN
}

4
if (deliverAreaLower == "usa" || deliverAreaLower == "great britain") {
return Locale.ENGLISH
}
if (deliverAreaLower == "france") {
return Locale.FRENCH
}
return Locale.ENGLISH
}

// Do
fun getDefaultLocale2(deliveryArea: String) = when (deliveryArea.toLowerCase()) {
"germany", "austria" -> Locale.GERMAN
"usa", "great britain" -> Locale.ENGLISH
"france" -> Locale.FRENCH
else -> Locale.ENGLISH
}

2. Top-Level (Extension) Functions for Utility Functions

//Don't
object StringUtil {
fun countAmountOfX(string: String): Int{
return string.length - string.replace("x", "").length
}
}
StringUtil.countAmountOfX("xFunxWithxKotlinx")

//Do
fun String.countAmountOfX(): Int {
return length - replace("x", "").length
}
"xFunxWithxKotlinx".countAmountOfX()

3. Named Arguments instead of Fluent Setter

//Don't
val config = SearchConfig()
.setRoot("~/folder")
.setTerm("game of thrones")
.setRecursive(true)
.setFollowSymlinks(true)

//Do
val config2 = SearchConfig2(
root = "~/folder",
term = "game of thrones",
recursive = true,
followSymlinks = true
)

5
4. apply() for Grouping Object Initialization

//Don't
val dataSource = BasicDataSource()
dataSource.driverClassName = "com.mysql.jdbc.Driver"
dataSource.url = "jdbc:mysql://domain:3309/db"
dataSource.username = "username"
dataSource.password = "password"
dataSource.maxTotal = 40
dataSource.maxIdle = 40
dataSource.minIdle = 4

//Do
val dataSource = BasicDataSource().apply {
driverClassName = "com.mysql.jdbc.Driver"
url = "jdbc:mysql://domain:3309/db"
username = "username"
password = "password"
maxTotal = 40
maxIdle = 40
minIdle = 4
}

5. Don’t Overload for Default Arguments

//Don't
fun find(name: String){
find(name, true)
}
fun find(name: String, recursive: Boolean){
}

//Do
fun find(name: String, recursive: Boolean = true){
}

6. Concisely Deal with Nullability

a. Avoid if-null Checks

//Don't
if (order == null || order.customer == null || order.customer.address == null){
throw IllegalArgumentException("Invalid Order")
}
val city = order.customer.address.city

//Do
val city = order?.customer?.address?.city ?: throw IllegalArgumentException("Invalid Order")

6
b. Avoid if-type Checks

//Don't
if (service !is CustomerService) {
throw IllegalArgumentException("No CustomerService")
}
service.getCustomer()

//Do
service as? CustomerService ?: throw IllegalArgumentException("No CustomerService")
service.getCustomer()

c. Avoid not-null Assertions !!

//Don't
order!!.customer!!.address!!.city

“You may notice that the double exclamation mark looks a bit rude: it’s almost like you’re yelling at the
compiler. This is intentional. The designers of Kotlin are trying to nudge you toward a better solution
that doesn’t involve making assertions that can’t be verified by the compiler.” Kotlin in Action by
Dmitry Jemerov and Svetlana Isakova

7. Leverage Value Objects

// Don't
fun send(target: String){}

// Do
fun send(target: EmailAddress){}
// expressive, readable, type-safe

data class EmailAddress(val value: String)


// Even better (Kotlin 1.3):
inline class EmailAddress(val value: String)

8. Concise Mapping with Single Expression Functions

// Don't
fun mapToDTO(entity: SnippetEntity): SnippetDTO {
val dto = SnippetDTO(
code = entity.code,
date = entity.date,
author = "${entity.author.firstName} ${entity.author.lastName}"
)
return dto
}

7
// Do
fun mapToDTO(entity: SnippetEntity) = SnippetDTO(
code = entity.code,
date = entity.date,
author = "${entity.author.firstName} ${entity.author.lastName}"
)
val dto = mapToDTO(entity)

// Do
fun SnippetEntity.toDTO() = SnippetDTO(
code = code,
date = date,
author = "${author.firstName} ${author.lastName}"
)
val dto = entity.toDTO()

9. Refer to Constructor Parameters in Property Initializers

// Don't
class UsersClient(baseUrl: String, appName: String) {
private val usersUrl: String
private val httpClient: HttpClient
init {
usersUrl = "$baseUrl/users"
val builder = HttpClientBuilder.create()
builder.setUserAgent(appName)
builder.setConnectionTimeToLive(10, TimeUnit.SECONDS)
httpClient = builder.build()
}
fun getUsers(){
//call service using httpClient and usersUrl
}
}

// Do
class UsersClient(baseUrl: String, appName: String) {
private val usersUrl = "$baseUrl/users"
private val httpClient = HttpClientBuilder.create().apply {
setUserAgent(appName)
setConnectionTimeToLive(10, TimeUnit.SECONDS)
}.build()
fun getUsers(){
//call service using httpClient and usersUrl
}
}

8
10. object for Stateless Interface Implementations

//Do
object StringToInstantConverter : Converter<String, Instant> {
private val DATE_FORMATTER = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss Z")
.withLocale(Locale.UK)
.withZone(ZoneOffset.UTC)

override fun convertToModel(value: String?, context: ValueContext?) = try {


Result.ok(Instant.from(DATE_FORMATTER.parse(value)))
} catch (ex: DateTimeParseException) {
Result.error<Instant>(ex.message)
}

override fun convertToPresentation(value: Instant?, context: ValueContext?) =


DATE_FORMATTER.format(value)
}

11. Destructuring

//Do
data class ServiceConfig(val host: String, val port: Int)
fun createServiceConfig(): ServiceConfig {
return ServiceConfig("api.domain.io", 9389)
}
//destructuring in action:
val (host, port) = createServiceConfig()

//Do
val map = mapOf("api.domain.io" to 9389, "localhost" to 8080)
for ((host, port) in map){
//...
}

12. Ad-Hoc Creation of Structs

listOf, mapOf and the infix function to can be used to create structs (like JSON) quite concisely

//Do
val customer = mapOf(
"name" to "Clair Grube",
"age" to 30,
"languages" to listOf("german", "english"),
"address" to mapOf(
"city" to "Leipzig",
"street" to "Karl-Liebknecht-Straße 1",
"zipCode" to "04107"
)
)

9
13. Kotlin Null Safety

https://www.callicoder.com/kotlin-nullable-types-null-safety/

?. / ?.let {..} / lateinit var .. / ?: / as? / requireNotNull() …..

The !! Operator can cause kotlin.KotlinNullPointerException. So, we should use other operators to
replace it.

https://viblo.asia/p/loai-bo-toan-tu-null-checks-trong-code-kotlin-cua-ban-V3m5WAyvZO7

14. Sealed Classes Instead of Exceptions in Kotlin

https://phauer.com/2019/sealed-classes-exceptions-kotlin/

// Definition
sealed class UserProfileResult {
data class Success(val userProfile: UserProfileDTO) : UserProfileResult()
data class Error(val message: String, val cause: Exception? = null) : UserProfileResult()
}

fun requestUserProfile(userId: String): UserProfileResult = try {


val userProfile = restTemplate.getForObject<UserProfileDTO>("http://domain.com/userProfiles/$userId")
UserProfileResult.Success(userProfile = userProfile)
} catch (ex: IOException) {
UserProfileResult.Error(
message = "Server request failed due to an IO exception. Id: $userId, Message: ${ex.message}",
cause = ex
)
} catch (ex: RestClientException) {
UserProfileResult.Error(
message = "Server request failed. Id: $userId. status code: ${(ex as?
RestClientResponseException)?.rawStatusCode}. body: ${(ex as?
RestClientResponseException)?.responseBodyAsString}",
cause = ex
)

// Usage
val avatarUrl = when (val result = client.requestUserProfile(userId)) {
is UserProfileResult.Success -> result.userProfile.avatarUrl
is UserProfileResult.Error -> "http://domain.com/defaultAvatar.png"

//The solution for the more complicated example could look like this:

when (val profileResult = client.requestUserProfile(userId)) {


is UserProfileResult.Success -> {
when (val imageResult = client.downloadImage(profileResult.userProfile.avatarUrl)){
is ImageDownloadResult.Success -> processImage(imageResult.image)
is ImageDownloadResult.Error -> queueForRetry(userId, imageResult.message)
}
}
is UserProfileResult.Error -> showMessageToUser(userId, profileResult.message)

10
}

15. let, run, also, apply, with

https://www.journaldev.com/19467/kotlin-let-run-also-apply-with

https://proandroiddev.com/the-difference-between-kotlins-functions-let-apply-with-run-and-else-
ca51a4c696b8

a. let

fun main(
fun main(args: Array<String>) {
var str = "Hello World"
str.let { println("$it!!") }
println(str)

}
//Prints
//Hello World!!
//Hello World

var a = 1
var b= 2

a = a.let { it + 2 }.let { val i = it + b


i // Block return value
}

println(a)
//5

b. Nesting let

var x = "Anupam"
x = x.let { outer ->
outer.let { inner ->
println("Inner is $inner and outer is $outer")
"Kotlin Tutorials Inner let"
}
"Kotlin Tutorials Outer let" // Block return value
}
println(x)

//Prints
//Inner is Anupam and outer is Anupam
//Kotlin Tutorials Outer let

c. let for null checks

11
var name : String? = "Kotlin let null check"
name?.let { println(it) } //prints: Kotlin let null check
name = null
name?.let { println(it) } //nothing happens

d. run

i. Similar to the let function, the run function also returns the last statement.

ii. Unlike let, the run function doesn’t support the it keyword.

var tutorial = "This is Kotlin Tutorial"


println(tutorial)
tutorial = run {
val _tutorial = "This is run function"
_tutorial//Block return value
}
println(tutorial)

//Prints
//This is Kotlin Tutorial
//This is run function

var p : String? = null


p?.let { println("p is $p") } ?: run {
println("p was null. Setting default value to: ")
p = "Kotlin"
}

println(p)

//Prints
//p was null. Setting default value to:
//Kotlin

e. also

i. Unlike let, it returns the original object instead of any new return data

ii. Like let, also uses it too

var m = 1
m = m.also { it + 1 }.also { it + 1 }
println(m) //prints 1

//differentiate between let and also


data class Person(var name: String, var tutorial : String)
var person = Person("Anupam", "Kotlin")

var l = person.let { it.tutorial = "Android" }


var al = person.also { it.tutorial = "Swift" }

println(l)
println(al)
println(person)

//Prints
//kotlin.Unit
//Person(name=Anupam, tutorial= Swift)
//Person(name=Anupam, tutorial= Swift)

12
f. Apply

data class Person(var name: String, var tutorial : String)


var person = Person("Anupam", "Kotlin")

person.apply { this.tutorial = "Swift" }


println(person)

//Person(name=Anupma, tutorial=Swift)

g. With

i. Like apply, with is used to change instance properties without the need to call dot operator
over the reference every time.

ii. The last expression of with function returns a result.

data class Person(var name: String, var tutorial : String)


var person = Person("Anupam", "Kotlin")

with(person)
{
name = "No Name"
tutorial = "Kotlin tutorials"
}
println(person) //Person(name=No Name, tutorial=Kotlin tutorials)

var xyz = with(person)


{
name = "No Name"
tutorial = "Kotlin tutorials"
val xyz = "End of tutorial"
xyz //Block return value
}
println(xyz) //End of tutorial

Apply, run, with: if we want to access to clas from inside of function, we can use this@class

13
Extension function is better than normal function because it reduces cheking null

bundle?.apply { //Extension function: check null once


putInt("some_int", 0)
putString("some_String", "")
}

with(bundle) { //Normal function: check null multiple times


this?.putInt("some_int", 0)
this?.putString("some_String", "")
}

16. Should use Kotlin lateinit var?

https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties-and-variables

Normally, properties declared as having a non-null type must be initialized in the constructor.
However, fairly often this is not convenient. For example, properties can be initialized through
dependency injection, or in the setup method of a unit test. In this case, you cannot supply a non-null
initializer in the constructor, but you still want to avoid null checks when referencing the property
inside the body of a class.

To handle this case, you can mark the property with the lateinit modifier:

class MyTest {
lateinit var subject: TestSubject

@SetUp fun setup() {


subject = TestSubject()
}

@Test fun test() {


subject.method() // dereference directly
}
}

Accessing a lateinit property before it has been initialized throws a special exception

fun main() {
User().showCompany()
}

class User() {
lateinit var company: Company

fun showCompany() {
println(company.toString())
}
}

class Company

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property company has


not been initialized

From Kotlin 1.2, To check whether a lateinit var has already been initialized, use .isInitialized

fun showCompany2() {
println(if (::company.isInitialized) {
company.toString()
} else {
"not Initialized"

14
})
}

=> Solution:

- Only use lateinit var for properties which can be initialized through dependency injection, or in the
setup method of a unit test

- Check isInitialized carefully

- When not required, please prioritize var company: Company? = null


(https://viblo.asia/p/co-nen-su-dung-kotlin-lateinit-var-hay-khong-LzD5dbneZjY)

17. Classes and Inheritance (https://kotlinlang.org/docs/reference/classes.html)

class Person(firstName: String) { ... }

class Person(val firstName: String, val lastName: String, var age: Int) { ... }

- If the constructor has annotations or visibility modifiers

class Customer public @Inject constructor(name: String) { ... }

i. Secondary Constructors

class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}

If the class has a primary constructor, each secondary constructor needs to delegate to the
primary constructor

class Person(val name: String) {


constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}

If you do not want your class to have a public constructor, you need to declare an empty
primary constructor with non-default visibility:

class DontCreateMe private constructor () { ... }

ii. Overriding Methods

open class Base {


//The override modifier is required for Derived.v()
open fun v() { ... }
// declaring a method with the same signature in a subclass is illegal, either with override or
without it
fun nv() { ... }
}

15
class Derived() : Base() {
override fun v() { ... }
}

open class AnotherDerived() : Base() {


final override fun v() { ... } //prohibit re-overriding
}

Overriding Rules: Supper<Base>

open class A {
open fun f() { print("A") }
fun a() { print("a") }
}

interface B {
fun f() { print("B") } // interface members are 'open' by default
fun b() { print("b") }
}

class C() : A(), B {


// The compiler requires f() to be overridden:
override fun f() {
super<A>.f() // call to A.f()
super<B>.f() // call to B.f()
}
}

18. Data Class

https://proandroiddev.com/kotlin-data-classes-enough-boilerplate-c4647e475485

https://www.callicoder.com/kotlin-data-classes/

https://kotlinlang.org/docs/reference/data-classes.html

Kotlin has a better solution for classes that are used to hold data/state. It’s called a Data Class. A
Data Class is like a regular class but with some additional functionalities.

The compiler automatically derives the following members from all properties declared in the primary
constructor: equals()/hashCode(), toString(), copy(), componentN()

i. Equals, toString()

class Customer(val id: Long, val name: String) {


var age: Int = 0
}
val customer1 = Customer(1, "John")
val customer2 = Customer(1, "John")

customer1.age = 10
customer2.age = 20

println(customer1)

16
println(customer2..toString())

println("Case 1: " + (customer1 == customer2))


println("Case 2: " + customer1.equals(customer2))

//Prints
com.unitestexample.myapplication.ExampleUnitTest$Customer@61e717c2
com.unitestexample.myapplication.ExampleUnitTest$Customer@66cd51c3
Case 1: false
Case 2: false

//Test on data class


data class Customer(val id: Long, val name: String) {
var age: Int = 0
}
//Prints
Customer(id=1, name=John)
Customer(id=1, name=John)
Case 1: true
Case 2: true

ii. Copy()

val customer3 = customer2.copy(name = "Duy")


println(customer3) // Customer(id=1, name=Duy)

iii. componentN()

println(customer1.component1()) //1
println(customer1.component2()) //John

19. Lambda Expressions: Important

https://www.baeldung.com/kotlin-lambda-expressions

https://www.journaldev.com/18835/kotlin-lambda-higher-order-functions

20. Companion Object

https://blog.egorand.me/where-do-i-put-my-constants-in-kotlin/

https://viblo.asia/p/nhung-dieu-nen-biet-khi-code-kotlin-cho-android-p1-bWrZneBwKxw

https://viblo.asia/p/chung-ta-nen-dinh-nghia-constants-o-dau-trong-kotlin-oOVlY1jol8W

21. Kotlin Interview Question

https://www.journaldev.com/20567/kotlin-interview-questions

III. Java convention


1. Source file basics
a. File name

The source file name consists of the case-sensitive name of the top-level class it contains
plus the .java extension.

17
b. File encoding: UTF-8
c. Whitespace characters: Aside from the line terminator sequence, the ASCII horizontal space
character (0x20) is the only whitespace character that appears anywhere in a source file. This
implies that:
- All other whitespace characters in string and character literals are escaped.
- Tab characters are not used for indentation.
d. Special escape sequences: For any character that has a special escape sequence (\b, \t, \n,
\f, \r, \", \' and \\), that sequence is used rather than the corresponding octal (e.g. \012) or
Unicode (e.g. \u000a) escape.
e. Non-ASCII characters:
2. Source file structure
A source file consists of, in order:
- License or copyright information, if present
- Package statement
- Import statements
- Exactly one top-level class
Exactly one blank line separates each section that is present.

3. Formatting
a. Braces: Braces are used where optional Braces, used with if, else, for, do and while
statements, even when the body is empty or contains only a single statement.
b. Nonempty blocks: K & R style
Braces follow the Kernighan and Ritchie style ("Egyptian brackets") for nonempty blocks and
block-like constructs:
- No line break before the opening brace.
- Line break after the opening brace.
- Line break before the closing brace.
- Line break after the closing brace, only if that brace terminates a statement or terminates the
body of a method, constructor, or named class. For example, there is no line break after the
brace if it is followed by else or a comma.
c. Whitespace:

Vertical Whitespace

- A single blank line may also appear anywhere it improves readability, for example
between statements to organize the code into logical subsections. A blank line before
the first member or initializer, or after the last member or initializer of the class, is
neither encouraged nor discouraged.
- Multiple consecutive blank lines are permitted, but never required (or encouraged).

Horizontal whitespace
✓ Separating any reserved word, such as if, for or catch, from an open parenthesis (()
that follows it on that line
✓ Separating any reserved word, such as else or catch, from a closing curly brace (})
that precedes it on that line
✓ Before any open curly brace ({), with two exceptions:

18
○ @SomeAnnotation({a, b}) (no space is used)
○ String[][] x = {{"foo"}}; (no space is required between {{, by item 8
below)
✓ On both sides of any binary or ternary operator. This also applies to the following
"operator-like" symbols:
○ the ampersand in a conjunctive type bound: <T extends Foo & Bar>
○ the pipe for a catch block that handles multiple exceptions: catch
(FooException | BarException e)
○ the colon (:) in an enhanced for ("foreach") statement
○ the arrow in a lambda expression: (String str) -> str.length()
✓ but not
○ the two colons (::) of a method reference, which is written like
Object::toString
○ the dot separator (.), which is written like object.toString()
✓ After ,:; or the closing parenthesis ()) of a cast
✓ On both sides of the double slash (//) that begins an end-of-line comment. Here, multiple
spaces are allowed, but not required.
✓ Between the type and variable of a declaration: List<String> list
✓ Optional just inside both braces of an array initializer
○ new int[] {5, 6} and new int[] { 5, 6 } are both valid
✓ Between a type annotation and [] or ....

This rule is never interpreted as requiring or forbidding additional space at the start or end of a line; it
addresses only interior space.

19

You might also like