pretty

Thursday, 14 July 2022

Http status codes: 401 vs 403

401 is about failed Authentication - I say who I am, do you believe me? If not - respond with 401 Unauthenticated

Example: login failed due to invalid credentials.


403 is about failed Authorization - my authentication is accepted, can I access this? If not - respond with 403 Unauthorized

Example: access to specific resource is not permitted with my role.

Wednesday, 24 February 2021

Kotlin contravariance samples

Contravariance is used to allow adding into collection values of type T and T subtypes. The collection itself, however, is allowed to contain items of type T and any super types of T (because of this it is not safe to get from this collection, with the exception of getting the root of type hierarchy Any?).

1. Use site variance

Sample: list with contravariance acts as a consumer.
It is safe to add into list the object of declared type B, or it's subtypes. It is not safe to retrieve object of type B from the list, because list of objects with super types of B may be provided. However, it is safe to retrieve the Kotlin most basic type Any? from the list.
 
open class A
open class B : A()
class C : B()

// Can receive list of B or its super types (A, Any, Any? types in this sample).
// Allows to put B or B subtypes into list (C in this sample).
// Cannot get fom List - except, can only get most generic type Any? from list.

fun listAsConsumer(list: MutableList<in B>) {
     // Type mismatch: inferred type is Any? but B was expected
     // val b: B = list[0] 
     
     // Type mismatch: inferred type is A but B was expected 
     // list.add(A())  
     
     // Cannot get fom List - except, can only get most generic type Any? from list.
     val any: Any? = list[0]
     
     // Allowed to add B or its subtypes into list
     list.add(B())
     list.add(C())
}

fun main() { 
    listAsConsumer(mutableListOf<A>(A())) 
} 

Sample: contravariance in Comparator.
Compare list of Ints using Comparator<Number>.
fun <T> Iterable<T>.sortedWithWrapper(comparator: Comparator<in T>): List<T> {
    return this.sortedWith(comparator)
}

fun main() { 
    val comparator: Comparator<Number> = Comparator { o1: Number, o2: Number ->
        o1.toDouble().compareTo(o2.toDouble())
    }
    val intList: List<Int> = listOf(4, 7)
    val result: List<Int> = intList.sortedWithWrapper(comparator)
}

The same sample without contravariance for Comparator is below. Without contravariance sortedWithWrapper() function could not return List<Int>, but only List<Number>. This happens because List<Int> is upcasted to List<Number>, to match the invariant T of the Comparator.
fun <T> Iterable<T>.sortedWithWrapper(comparator: Comparator<T>): List<T> {
    return this.sortedWith(comparator)
}

fun main() { 
    val comparator: Comparator<Number> = Comparator { o1: Number, o2: Number ->
        o1.toDouble().compareTo(o2.toDouble())
    }
    val intList: List<Int> = listOf(4, 7)
    
    // Type mismatch: inferred type is List<Number> but List<Int> was expected
    // val result: List<Int> = intList.sortedWithWrapper(comparator)
    val result: List<Number> = intList.sortedWithWrapper(comparator)
}


2. Declaration site variance

Declaration site variance is available in Kotlin, unlike Java where only use-site variance is possible.

Sample: Class with contravariance. Consumer cannot have contravariant type T in invariant or out positions.
open class A
open class B : A() 
class C : B() 
 
class Consumer<in T> {
    
    // Type parameter T is declared as 'in' but occurs in 'invariant' 
    // position in type T?
    // var x: T? = null

    fun consume(x: T) {}  
     
    // Ok, List is defined as List<out T>,
    // so it is a producer for this Consumer class
    fun consume(x: List<T>) {} 
     
    // Type parameter T is declared as 'in' but occurs in 'invariant' position 
    // in type Array<T>
    // fun consume(x: Array<T>) {} 
     
    // Type parameter T is declared as 'in' but occurs in 'out' position
    // in type T?
    // fun produce() : T? { return x }   
}

fun main() {   
    // Consumer of B (can be assigned Consumer of supertypes (A, Any, Any?)),
    // but now minimum B or its subtypes are accepted into consume()
    val consumerB: Consumer<B> = Consumer<A>() 
    consumerB.consume(C())
    
    // Type mismatch: inferred type is A but B was expected
    // consumerC.consume(A()) 
    
    // Type mismatch: inferred type is Consumer<B> but Consumer<A> was expected
    // val consumerA: Consumer<A> = Consumer<B>()  
}

Consumer class with contravariant T cannot accept another consumer with contravariant T.
class AnotherConsumer<in T> {
     fun consume() {}
}

class Consumer<in T> {
   
    // Type parameter T is declared as 'in' but occurs in 'out' position
    // in type AnotherConsumer
    // fun consume(x: AnotherConsumer<T>) {}  
}

This is due to AnotherConsumer can be extended by the class that cancels the contravariant 'in' modifier:
open class AnotherConsumer<in T> {
     open fun consume(x: T) {}
}

class AnotherConsumerChild: AnotherConsumer<T>() {
    var x: T? = null
    
    override fun consume(x: T) {
        this.x = x
    }  
    
    fun getT(): T? {
        return x
    }
}

Allowing to accept another contravariant consumer would lead to ClassCastException.
As an example of the above, in this snippet AnotherConsumerC would be called with the wrong type A (omitting compiler variance checking with @UnsafeVariance annotation).
open class A
open class B : A() 
class C : B() {
    fun c() {}
} 

open class AnotherConsumer<in T> {
     open fun consume(x: T) {}
}

// Overrides contravariance in parent type
class AnotherConsumerC: AnotherConsumer<C>() {
    override fun consume(x: C) {
        x.c()
    }  
}

open class Consumer<in T> {  
    open fun consume(anotherConsumer: AnotherConsumer<@UnsafeVariance T>) {}  
}  

open class ConsumerA: Consumer<A>() {  
    override fun consume(anotherConsumer: AnotherConsumer<A>) {
        anotherConsumer.consume(A())
    }  
}  

fun main() {   
    val consumerA = ConsumerA()
    
    // Allowed because parent Consumer<in T> is contravariant
    val consumerC: Consumer<C> = consumerA
    
    // Produces Exception in thread "main" 
    // java.lang.ClassCastException: A cannot be cast to C
    consumerC.consume(AnotherConsumerC())
}

Wednesday, 24 July 2019

Android ScrollView child width is larger than the screen (Api 21 bug)


On Android Lollipop this layout would produce a screen where children views of the ScrollView would have incorrect width.

This is how the view looks on api level 21 emulator


And this the correct result, from api 27


To fix this ScrollView issue on api 21 devices, the child view "android:layout_margin" should be replaced with "android:padding". If it is not acceptable for the layout to have padding instead of margin (for example due to background color or click area placement) - padding can be set on parent container views. rows="20"