blow your mind

The BYM series aims to share not only technology but also ideas, not just being a porter of code.

1. Int replaces Integer

For some of the interfaces returned from the back end, and for list filtering definitions, we usually define data of type Integer. Such as:

Integer userId;// The user ID returned by the backend

Integer state;//null,0,1,2,3 // state filtering for some lists
Copy the code

In KT, let’s not use the Integer wrapper type anymore. Replace Kt directly with Kt’s base type, Kt. If you insist on using the Integer type, type incompatibility will occur.

Such as:

// Enumerate class Java files
public enum CheckStatus {
    WAIT(1),...private final Integer value;
    CheckStatus(Integer value) {
        this.value = value; }}/ / kt file
var checkStatus: Integer? = null

1 -> checkStatus = CheckStatus.WAIT.value
                   ~~~~~~~~~~~~~~~~~~~~~~
Type mismatch.
Required:
Integer?
Found:
Int!
Copy the code

I wonder why the type mismatch was reported here. Because kt itself does not have the base type int, double… , kt automatically encapsulates kt’s own wrapper types Int, Short, Long, Float, Double, etc., when kt processes Java data types. So while we continue to use Integer in our KT file, the Integer in Java that we get through the KT code call is wrapped as an Int, so it doesn’t match the Integer type.

We can simply replace the Integer with an Int.

2. Val and var in interface

When we define an interface, we want the classes that inherit the interface to return a field as a key field. For example: list, filter box display text, we generally define in Java


interface SimpleText{
    String getTitle(a);
}
// Implement subclasses
class TestText implements SimpleText{
    String name
     @Override
    public String getTitle(a) {
        returnname; }}Copy the code

In KT, if we only need to define a get method, we declare the field with val, because val means constant, but val modifies the field to allow initialization once. Satisfies our requirements for the scenario above, so it should be written this way

interface SimpleText {
    val title: String
}
// Implement subclasses
// In Java, the implementation is the same as above
/ / kt as follows
class TestText : SimpleText {
    var name: String = ""
    override val title: String
        get() = name
}
Copy the code

So if you define the field in interface as var, that means the parameter is mutable, then you need to override the get/set method in the implementation subclass.

3. Associated objects and newInstance

When using a Fragment, we often define newInstance to create a Fragment and pass parameters to the Fragment. As shown below.

    public class MyFragment extends Fragment {
    private static final String ARG_CAUGHT = "myFragment_caught";
    public static MyFragment newInstance(Pokemon caught) {
        Bundle args = new Bundle();
        args.putSerializable(ARG_CAUGHT, caught);

        MyFragment fragment = new MyFragment();
        fragment.setArguments(args);
        returnfragment; }... }Copy the code

So in kt, we need to use

class MyFragment: Fragment() {companion object{
    private val ARG_CAUGHT = "myFragment_caught"
    
    @JvmStatic This annotation is required to call myfragment.newinstance () in Java
    fun newInstance(caught: Pokemon):MyFragment{
      val args: Bundle = Bundle()
      args.putSerializable(ARG_CAUGHT, caught)
      val fragment = MyFragment()
      fragment.arguments = args
      return fragment
    }
    ...
  }
  fun test(a){
     valcaught = arguments? .getString(ARG_CAUGHT) } }Copy the code

You can see that there is a constant ARG_CAUGHT in the example, which can also be used in MyFragment.

Matters needing attention:

  • On the JVM platform, if you use the @jVMStatic annotation, you can generate the members of the associated object as true static methods and fields
  • The initialization of the associated object matches the semantics of the Java static initializer when the corresponding class is loaded (parsed).

4 Parameter type must not include a type variable or wildcard

Error reported in Log while modifying Retrofit + Kotlin request interface

Parameter type must not include a type variable or wildcard: 

java.util.Map<java.lang.String, ? extend okhttp3.RequestBody> (parameter #1)
Copy the code

The error scenario is as follows:

@Multipart
@POST("xxx/addTestProject")
fun addTestProject(@PartMap paramsMap: Map<String, RequestBody>?):Observable<BaseApiEntity<String? >? >Copy the code

Nothing wrong at first glance, but at runtime, RequestBody, as an abstract Java class, is treated like Any by KT, where the KT syntax does not allow you to declare a wildcard parameter. This time only requires the annotation @jVMSuppresswildcards in front of the parameter

Here we can extend the two annotations JvmWildcard and JvmSuppressWildcards needed in the mutual call between Android and KT

@JvmWildcard

This annotation deals mainly with generic parameters, which introduces two new concepts: contravariant and covariant. Since the Java language does not support covariance, to ensure safe calls to each other, you can use the Kotlin compiler to generate generic arguments in the form of wildcards (? extends …) .

Take a look at this code:

class Box<out T> (val value: T)

interface Base
class Derived : Base

fun boxDerived(value: Derived) :Box<Derived> = Box(value)
fun unboxBase(box: Box<Base>): Base = box.value
Copy the code

In normal thinking, the following two methods should be converted to Java code like this:

Box<Derived> boxDerived(Derived value) { …… }
Base unboxBase(Box<Base> box) { …… }
Copy the code

The problem is that Kotlin generics support type variation. In Kotlin we can write unboxBase(Box(Derived())), whereas in the Java language generic parameter types are immutable.

The correct conversion to Java code would look like this:

Base unboxBase(Box<? extends Base> box) { …… }
Copy the code

In order for such a transformation to be generated correctly, we need to add the above annotation at the position of the generic parameter:

fun unboxBase(box: Box<@JvmWildcard Base>): Base = box.value
Copy the code

@JvmSuppressWildcards

This annotation is the opposite of @jVMwildcard. It inhibits the generation of wildcard generics, so we can add this annotation to avoid generating generics when we don’t need them.

fun unboxBase(box: Box<@JvmSuppressWildcards Base>): Base = box.value
// The generated code is equivalent to the following Java code
Base unboxBase(Box<Base> box) { …… }
Copy the code

Using these annotations correctly, you can smooth out the differences between Kotlin and Java generics handling and avoid safe conversion issues.

I recommend that you take a look at Kotlin’s generics at the end of this article, which is a little more accessible than the Kotlin website

5. To be updated

reference

  • Kotlin’s generics
  • Proper use of Kotlin annotations, compatible with Java code
  • kotlincn.net
  • Using the NewInstance Pattern in Kotlin