JavaRush /Блоги Java /Random-TG /Хусусиятҳои Java 8 - Роҳнамои ниҳоӣ (Қисми 1)
0xFF
Сатҳи
Донецк

Хусусиятҳои Java 8 - Роҳнамои ниҳоӣ (Қисми 1)

Дар гурӯҳ нашр шудааст
Қисми якуми тарҷумаи мақолаи Java 8 Features – The ULTIMATE Guide . Қисми дуюм ин ҷост (пайванд метавонад тағир ёбад). Хусусиятҳои Java 8 - Роҳнамои ниҳоӣ (Қисми 1) - 1 Эзоҳ аз муҳаррир : Ин мақола дар ҳоле нашр шудааст, ки Java 8 барои омма дастрас буд ва ҳама нишонаҳо аз он шаҳодат медиҳанд, ки ин воқеан як versionи асосӣ аст. Дар ин ҷо мо дастурҳои Java Code Geeks-ро ба таври фаровон пешниҳод кардем, ба монанди Playing with Java 8 – Lambdas and Concurrency , Java 8 Date and Time Guide API: LocalDateTime ва Синфи абстрактӣ бо интерфейси Java 8 Era . Мо инчунин ба 15 дастури Java 8-ро аз дигар манбаъҳо мепайвандем . Албатта, мо ба баъзе нуқсонҳо назар мекунем, ба монанди тарафи торикии Java 8 . Ҳамин тавр, вақти он расидааст, ки ҳамаи хусусиятҳои асосии Java 8-ро дар як ҷо ҷамъоварӣ кунед, то ба шумо роҳат бошад. Баҳра баред!

1. Муқаддима

Бешубҳа, интишори Java 8 бузургтарин рӯйдоди пас аз Java 5 аст (ҳанӯз дар соли 2004 бароварда шуда буд). Он ба Java бисёр хусусиятҳои навро ҳам дар забон, компилятор, китобхонаҳо, асбобҳо ва JVM (Java Virtual Machine) овард. Дар ин дастур, мо ин тағиротҳоро дида мебароем ва ҳолатҳои гуногуни истифодаро бо мисолҳои воқеии ҳаёт нишон медиҳем. Дастур аз якчанд қисм иборат аст, ки ҳар яки онҳо як ҷанбаи мушаххаси платформаро баррасӣ мекунанд:
  • Забон
  • Мураттиб
  • Китобхонахо
  • Воситаҳо
  • Муҳити корӣ (JVM)

2. Хусусиятҳои нав дар Java 8

Дар ҳар сурат, Java 8 як нашри асосӣ аст. Мо гуфта метавонем, ки он аз сабаби татбиқи хусусиятҳое, ки ҳар як таҳиягари Java ҷустуҷӯ мекард, ин қадар тӯл кашид. Дар ин бахш мо бештари онҳоро баррасӣ хоҳем кард.

2.1. Ламбдаҳо ва интерфейсҳои функсионалӣ

Ламбдаҳо (инчунин ҳамчун усулҳои хусусӣ ё беном маълуманд) бузургтарин ва аз ҳама интизоршавандаи забон дар тамоми versionи Java 8 мебошанд. Онҳо ба мо имкон медиҳанд, ки функсияро ҳамчун аргументи метод муайян кунем (бо эълон кардани функсия дар атрофи он) ё codeро ҳамчун маълумот муайян кунем. : мафҳумҳое, ки ҳар як таҳиягари функсионалӣ бо онҳо шинос аст . Бисёр забонҳо дар платформаи JVM (Groovy, Scala , ...) аз рӯзи аввал ламбдаҳо доштанд, аммо таҳиягарони Java илоҷе надоштанд, ки ламбдаҳоро тавассути синфҳои номаълум муаррифӣ кунанд. Муҳокимаи тарҳи ламбдаҳо аз мардум вақт ва кӯшиши зиёдеро талаб мекард. Аммо дар ниҳоят созишҳо пайдо шуданд, ки боиси пайдоиши тарҳҳои нави мухтасар гардиданд. Дар соддатарин шакли худ, ламбдаро метавон ҳамчун рӯйхати бо вергул ҷудошудаи параметрҳо, аломати –> ва бадан муаррифӣ кард. Барои намуна:
Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) )
Дар хотир доред, ки навъи аргументи e аз ҷониби компилятор муайян карда мешавад. Илова бар ин, шумо метавонед навъи параметрро тавассути печонидани параметр дар қавс ба таври возеҳ муайян кунед. Барои намуна:
Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) );
Дар сурати мураккабтар будани бадани лямбда, онро метавон бо қавсҳои ҷингила печонд, ки ба таърифи муқаррарии функсия дар Java монанд аст. Барои намуна:
Arrays.asList( "a", "b", "d" ).forEach( e -< {
    System.out.print( e );
    System.out.print( e );
} );
Ламбда метавонад ба аъзоёни синф ва тағирёбандаҳои маҳаллӣ муроҷиат кунад (вобаста аз он ки finalба майдон дастрас аст ё не, зангро ба таври возеҳ самаранок мегардонад). Масалан, ин 2 порча баробаранд:
String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
ВА:
final String separator = ",";
Arrays.asList( "a", "b", "d" ).forEach(
    ( String e ) -> System.out.print( e + separator ) );
Ламбдаҳо метавонанд арзиш баргардонанд. Навъи бозгашт аз ҷониби компилятор муайян карда мешавад. returnАгар бадани ламбда аз як сатр иборат бошад, эъломия талаб карда намешавад. Ду пораи codeи дар поён овардашуда баробаранд:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
ВА:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
    int result = e1.compareTo( e2 );
    return result;
} );
Таҳиягарони забон муддати тӯлонӣ дар бораи он фикр мекарданд, ки чӣ гуна функсияҳои мавҷударо ба ламбда мувофиқ созанд. Дар натиҷа, консепсияи интерфейси функсионалӣ пайдо шуд. Интерфейси функсионалӣ интерфейсест, ки танҳо як усул дорад. Дар натиҷа, он метавонад ба таври ғайримустақим ба ифодаи лямбда табдил дода шавад. java.lang.Runnableва java.util.concurrent.Callableду намунаи бузурги интерфейсҳои функсионалӣ. Дар амал интерфейсҳои функсионалӣ хеле нозуканд: агар касе ба таърифи интерфейс ҳатто як усули дигарро илова кунад, он дигар функсионалӣ нахоҳад буд ва раванди тартибдиҳӣ ба анҷом намерасад. Барои пешгирӣ кардани ин ноустуворӣ ва ба таври возеҳ муайян кардани ҳадафи интерфейс ҳамчун функсионалӣ, дар Java 8 шарҳи махсус илова карда шуд @FunctionalInterface(ҳамаи интерфейсҳои мавҷуда дар китобхонаи Java шарҳи @FunctionalInterfaceро гирифтанд). Биёед ба ин таърифи оддии интерфейси функсионалӣ назар кунем:
@FunctionalInterface
public interface Functional {
    void method();
}
Як чизро бояд дар хотир дошт: усулҳои пешфарз ва усулҳои статикӣ принсипи интерфейси функсионалӣ вайрон намешаванд ва онҳоро эълон кардан мумкин аст:
@FunctionalInterface
public interface FunctionalDefaultMethods {
    void method();

    default void defaultMethod() {
    }
}
Ламбдаҳо маъмултарин хусусияти Java 8 мебошанд. Онҳо тамоми потенсиали барои ҷалби бештари таҳиягарон ба ин платформаи олиҷаноб ва дастгирии оқилона барои хусусиятҳо дар Java холисро доранд. Барои маълумоти муфассал, лутфан ба ҳуҷҷатҳои расмӣ муроҷиат кунед .

2.2. Интерфейсҳои пешфарз ва усулҳои статикӣ

Java 8 таърифи интерфейсҳоро бо ду мафҳуми нав васеъ кард: усули пешфарз ва усули статикӣ. Усулҳои пешфарз интерфейсҳоро то андозае ба хислатҳо монанд мекунанд, аммо ҳадафи каме дигарро иҷро мекунанд. Онҳо ба шумо имкон медиҳанд, ки усулҳои навро ба интерфейсҳои мавҷуда бидуни вайрон кардани мутобиқати ақиб барои versionҳои қаблан навишташудаи ин интерфейсҳо илова кунед. Фарқи байни усулҳои пешфарз ва усулҳои абстрактӣ дар он аст, ки усулҳои абстрактӣ бояд татбиқ карда шаванд, дар ҳоле ки усулҳои пешфарз нестанд. Ба ҷои ин, ҳар як интерфейс бояд татбиқи ба истилоҳ пешфарзро таъмин намояд ва ҳамаи наслҳо онро ба таври нобаёнӣ қабул мекунанд (бо имкони бекор кардани ин татбиқи пешфарз дар ҳолати зарурӣ). Биёед мисоли зеринро дида бароем.
private interface Defaulable {
    // Интерфейсы теперь разрешают методы по умолчанию,
    // клиент может реализовывать  (переопределять)
    // or не реализовывать его
    default String notRequired() {
        return "Default implementation";
    }
}

private static class DefaultableImpl implements Defaulable {
}

private static class OverridableImpl implements Defaulable {
    @Override
    public String notRequired() {
        return "Overridden implementation";
    }
}
Интерфейс Defaulableусули пешфарзро notRequired()бо истифода аз калимаи калидӣ defaultҳамчун як қисми таърифи усул эълон мекунад. Яке аз синфҳо, DefaultableImpl, ин интерфейсро амалӣ мекунад, ки усули пешфарзро тавре боқӣ мемонад. Синфи дигар, OverridableImpl, татбиқи пешфарзро бекор мекунад ва худашро таъмин мекунад. Боз як хусусияти ҷолибе, ки дар Java 8 ҷорӣ шудааст, ин аст, ки интерфейсҳо метавонанд усулҳои статикиро эълон кунанд (ва татбиқи онҳоро пешниҳод кунанд). Инак як мисол:
private interface DefaulableFactory {
    // Interfaces now allow static methods
    static Defaulable create( Supplier<Defaulable> supplier ) {
        return supplier.get();
    }
}
Як порчаи хурди code усули пешфарз ва усули статикиро аз мисоли дар боло овардашуда муттаҳид мекунад:
public static void main( String[] args ) {
    Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
    System.out.println( defaulable.notRequired() );

    defaulable = DefaulableFactory.create( OverridableImpl::new );
    System.out.println( defaulable.notRequired() );
}
Натиҷаи консол аз ин барнома чунин менамояд:
Default implementation
Overridden implementation
Татбиқи усули пешфарз дар JVM хеле самаранок аст ва даъвати метод бо дастурҳои bytecode дастгирӣ карда мешавад. Усулҳои пешфарз имкон доданд, ки интерфейсҳои мавҷудаи Java бидуни вайрон кардани раванди тартибдиҳӣ таҳаввул кунанд. Намунаҳои хуб ин усулҳои зиёде ҳастанд, ки ба интерфейс илова карда шудаанд java.util.Collection: stream(), parallelStream(), forEach(), removeIf(), , ... Ҳарчанд пурқувватанд, усулҳои пешфарз бояд бо эҳтиёт истифода шаванд: пеш аз эълон кардани усули пешфарз, шумо бояд ду маротиба дар бораи он фикр кунед, ки оё ин воқеан зарур аст, зеро ин метавонад боиси он шавад. барои тартиб додани номуайянӣ ва хатогиҳо дар иерархияҳои мураккаб. Барои маълумоти муфассал, лутфан ба ҳуҷҷатҳо муроҷиат кунед .

2.3. Усулҳои истинод

Усулҳои истинод синтаксиси муфидро барои истинод ба усулҳо ё созандагони синфҳои Java ё an objectҳои (мисолҳо) мавҷуда амалӣ мекунанд. Якҷоя бо ифодаҳои лямбда, усулҳои истинод конструксияҳои забонро паймон ва мухтасар мегардонанд ва онро ба қолаб асос мекунанд. Дар зер синф Carҳамчун намунаи таърифҳои усулҳои гуногун оварда шудааст, биёед чор намуди усулҳои истинодро таъкид кунем:
public static class Car {
    public static Car create( final Supplier<Car> supplier ) {
        return supplier.get();
    }

    public static void collide( final Car car ) {
        System.out.println( "Collided " + car.toString() );
    }

    public void follow( final Car another ) {
        System.out.println( "Following the " + another.toString() );
    }

    public void repair() {
        System.out.println( "Repaired " + this.toString() );
    }
}
Усули аввалини истинод истинод ба созанда бо синтаксис Class::newё алтернатива барои генерикҳо мебошад Class< T >::new. Дар хотир доред, ки конструктор ҳеҷ гуна далел надорад.
final Car car = Car.create( Car::new );
final List<Car> cars = Arrays.asList( car );
Варианти дуюм истинод ба усули статикӣ бо синтаксис мебошад Class::static_method. Дар хотир доред, ки усул маҳз як параметри навъи Car.
cars.forEach( Car::collide );
Навъи сеюм истинод ба усули намунаи an objectи ихтиёрии навъи муайян бо синтаксис мебошад Class::method. Дар хотир доред, ки ҳеҷ гуна далелҳо аз ҷониби усул қабул карда намешаванд.
cars.forEach( Car::repair );
Ва навъи охирин, чорум истинод ба усули намунаи синфи мушаххас бо синтаксис мебошад instance::method. Лутфан қайд кунед, ки усул танҳо як параметри навъи Car.
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
Иҷрои ҳамаи ин мисолҳо ҳамчун барномаҳои Java натиҷаи консолии зеринро ба вуҷуд меорад (маълумоти синф Carметавонад фарқ кунад):
Collided com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Repaired com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Following the com.javacodegeeks.java8.method.references.MethodReferences$Car@7a81197d
Барои маълумоти муфассал ва тафсилоти усулҳои истинод, лутфан ба ҳуҷҷатҳои расмӣ муроҷиат кунед .

2.4. Аннотацияҳои такрорӣ

Азбаски Java 5 дастгирии эзоҳҳоро ҷорӣ кард , ин хусусият хеле маъмул гаштааст ва хеле васеъ истифода мешавад. Аммо, яке аз маҳдудиятҳои истифодаи эзоҳҳо дар он буд, ки як эзоҳро дар як ҷой на бештар аз як маротиба эълон кардан мумкин нест. Java 8 ин қоидаро вайрон мекунад ва эзоҳҳои такрориро ҷорӣ мекунад. Ин имкон медиҳад, ки ҳамон эзоҳҳо дар ҷойе, ки эълон карда шудаанд, чанд маротиба такрор карда шаванд. Эзоҳҳои такрорӣ бояд худро бо истифода аз annotation шарҳ диҳанд @Repeatable. Дар асл, ин на он қадар тағирот дар забон аст, балки як ҳиллаест, ки компилятор аст, дар ҳоле ки техника ҳамоно боқӣ мемонад. Биёед як мисоли оддиро бубинем:
package com.javacodegeeks.java8.repeatable.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

public class RepeatingAnnotations {
    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    public @interface Filters {
        Filter[] value();
    }

    @Target( ElementType.TYPE )
    @Retention( RetentionPolicy.RUNTIME )
    @Repeatable( Filters.class )
    public @interface Filter {
        String value();
    };

    @Filter( "filter1" )
    @Filter( "filter2" )
    public interface Filterable {
    }

    public static void main(String[] args) {
        for( Filter filter: Filterable.class.getAnnotationsByType( Filter.class ) ) {
            System.out.println( filter.value() );
        }
    }
}
Тавре ки мо мебинем, синф бо @Repeatable (Филтрҳо ) Filterшарҳ дода шудааст . танҳо соҳиби эзоҳҳост , аммо компилятори Java мекӯшад ҳузури онҳоро аз таҳиягарон пинҳон кунад. Ҳамин тариқ, интерфейс дорои эзоҳҳое мебошад , ки ду маротиба эълон карда мешаванд (бидуни зикр ). Reflection API инчунин усули навро барои баргардонидани эзоҳҳои такрории баъзе намудҳо таъмин мекунад (дар хотир доред, ки Filterable. .getAnnotation( Филтрҳо. ) намунаи аз ҷониби компилятор воридшударо бармегардонад ). Натиҷаи барнома чунин хоҳад буд: classFiltersFilterFilterableFilterFiltersgetAnnotationsByType()classclassFilters
filter1
filter2
Барои маълумоти муфассал, лутфан ба ҳуҷҷатҳои расмӣ муроҷиат кунед .

2.5. Баррасии навъи беҳтаршуда

Компилятори Java 8 беҳбудиҳои зиёди хулосаҳоро гирифтааст. Дар бисёр мавридҳо, параметрҳои навъи возеҳро компилятор муайян кардан мумкин аст ва ба ин васила codeро тоза мекунад. Биёед як мисолро дида бароем:
package com.javacodegeeks.java8.type.inference;

public class Value<T> {
    public static<T> T defaultValue() {
        return null;
    }

    public T getOrDefault( T value, T defaultValue ) {
        return ( value != null ) ? value : defaultValue;
    }
}
Ва ин аст истифода бо type Value<String>:
package com.javacodegeeks.java8.type.inference;

public class TypeInference {
    public static void main(String[] args) {
        final Value<String> value = new Value<>();
        value.getOrDefault( "22", Value.defaultValue() );
    }
}
Параметри намуд Value.defaultValue()ба таври худкор муайян карда мешавад ва ба таври возеҳ пешниҳод кардан лозим нест. Дар Java 7, ҳамон мисол тартиб дода намешавад ва бояд ҳамчун <NOBR>Value.<String>defaultValue()</NOBR> аз нав навишта шавад.

2.6. Дастгирии тавсеаи эзоҳ

Java 8 контекстро васеъ мекунад, ки эзоҳҳоро истифода бурдан мумкин аст. Дар айни замон, қариб ҳама чиз метавонад эзоҳ дошта бошад: тағирёбандаҳои маҳаллӣ, намудҳои умумӣ, суперклассҳо ва интерфейсҳои амалӣ, ҳатто истисноҳои усул. Дар зер чанд мисол оварда шудааст:
package com.javacodegeeks.java8.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;

public class Annotations {
    @Retention( RetentionPolicy.RUNTIME )
    @Target( { ElementType.TYPE_USE, ElementType.TYPE_PARAMETER } )
    public @interface NonEmpty {
    }

    public static class Holder<@NonEmpty T> extends @NonEmpty Object {
        public void method() throws @NonEmpty Exception {
        }
    }

    @SuppressWarnings( "unused" )
    public static void main(String[] args) {
        final Holder<String> holder = new @NonEmpty Holder<String>();
        @NonEmpty Collection<@NonEmpty String> strings = new ArrayList<>();
    }
}
ElementType.TYPE_USEва ElementType.TYPE_PARAMETERду намуди нави элемент барои тавсифи контексти мувофиқи эзоҳ. Annotation Processing APIинчунин барои шинохтани навъҳои нави эзоҳҳо дар Java тағйироти ночиз ворид кардааст.

3. Хусусиятҳои нав дар компилятори Java

3.1. Номҳои параметрҳо

Дар тӯли вақт, таҳиягарони Java роҳҳои гуногуни нигоҳ доштани номҳои параметрҳои методро дар bytecodeи Java ихтироъ карданд , то онҳоро дар вақти корӣ дастрас кунанд (масалан, китобхонаи Paranamer ). Ниҳоят, Java 8 ин вазифаи душворро дар забон (бо истифода аз API Reflection and method Parameter.getName()) ва bytecode (бо истифода аз аргументи нави компилятор javac:) эҷод мекунад –parameters.
package com.javacodegeeks.java8.parameter.names;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class ParameterNames {
    public static void main(String[] args) throws Exception {
        Method method = ParameterNames.class.getMethod( "main", String[].class );
        for( final Parameter parameter: method.getParameters() ) {
            System.out.println( "Parameter: " + parameter.getName() );
        }
    }
}
Агар шумо ин синфро бе истифодаи аргумент тартиб диҳед –parametersва он гоҳ барномаро иҷро кунед, шумо чунин чизро хоҳед дид:
Parameter: arg0
Ҳангоми ба компилятор интиқол додани параметр –parameters, баромади барнома гуногун хоҳад буд (номи воқеии параметр нишон дода мешавад):
Parameter: args
Барои корбарони пешрафтаи Maven, аргументи -parameters метавонад бо истифода аз қисмат ба компиляция илова карда шавад maven-compiler-plugin:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
    <configuration>
    <compilerArgument>-parameters</compilerArgument>
    <source>1.8</source>
    <target>1.8</target>
    </configuration>
</plugin>
Барои санҷидани мавҷудияти номҳои параметрҳо isNamePresent()усули мувофиқе мавҷуд аст, ки синф пешниҳод кардааст Parameter.

4. Асбобҳои нави Java

Java 8 бо маҷмӯи нави асбобҳои сатри фармон меояд. Дар ин бахш мо ҷолибтаринҳои онҳоро дида мебароем.

4.1. Муҳаррики Нашорн: jjs

jjs як муҳаррики мустақor Nashorn аст, ки ба сатри фармон асос ёфтааст. Он рӯйхати файлҳои codeи ибтидоии JavaScript-ро мегирад ва онҳоро иҷро мекунад. Масалан, биёед файли func.js- ро бо мундариҷаи зерин эҷод кунем:
function f() {
     return 1;
};

print( f() + 1 );
Барои иҷро кардани ин файл биёед онро ҳамчун аргумент ба jjs гузаронем :
jjs func.js
Натиҷаи консол чунин хоҳад буд:
2
Барои тафсилоти бештар ба ҳуҷҷатҳо нигаред .

4.2. Таҳлилгари вобастагии синф: jdeps

jdeps як воситаи воқеии бузурги сатри фармон аст. Он вобастагии сатҳи баста ё синфро барои синфҳои Java нишон медиҳад. Он файли .class , ҷузвдон ё файли JAR-ро ҳамчун вуруд қабул мекунад. Бо нобаёнӣ , jdeps вобастагиҳоро ба баромади стандартӣ (консол) мебарорад. Ҳамчун мисол, биёед гузориши вобастагии китобхонаи маъмули Spring Framework -ро бубинем . Барои кӯтоҳ нигоҳ доштани мисол, биёед вобастагиҳоро барои файли JAR танҳо бубинем org.springframework.core-3.0.5.RELEASE.jar.
jdeps org.springframework.core-3.0.5.RELEASE.jar
Ин фармон хеле зиёд баромад мекунад, аз ин рӯ мо танҳо як қисми баромадро таҳлил хоҳем кард. Вобастагӣ аз рӯи бастаҳо гурӯҳбандӣ карда мешавад. Агар вобастагӣ мавҷуд набошад, ёфт нашуд нишон дода мешавад .
org.springframework.core-3.0.5.RELEASE.jar -> C:\Program Files\Java\jdk1.8.0\jre\lib\rt.jar
   org.springframework.core (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.io
      -> java.lang
      -> java.lang.annotation
      -> java.lang.ref
      -> java.lang.reflect
      -> java.util
      -> java.util.concurrent
      -> org.apache.commons.logging                         not found
      -> org.springframework.asm                            not found
      -> org.springframework.asm.commons                    not found
   org.springframework.core.annotation (org.springframework.core-3.0.5.RELEASE.jar)
      -> java.lang
      -> java.lang.annotation
      -> java.lang.reflect
      -> java.util
Барои маълумоти муфассал, лутфан ба ҳуҷҷатҳои расмӣ муроҷиат кунед .
Шарҳҳо
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION