APEX-tutorial: Apex-programmeringsklasse og -kodningseksempler
Hvad er Apex i Salesforce?
Apex er et objektorienteret og stærkt typet programmeringssprog udviklet af Salesforce til at bygge Software as a Service (SaaS) og Customer Relationship Management (CRM). Apex hjælper udviklere med at skabe tredjeparts SaaS-applikationer og tilføje forretningslogik til systemhændelser ved at levere back-end-databaseunderstøttelse og klient-server-grænseflader.
Apex hjælper udviklere med at føje forretningslogik til systembegivenhederne såsom knapklik, relaterede registreringsopdateringer og Visualforce-sider. Apex har en lignende syntaks til Java. Tilmeld dig Salesforce for at lære, hvordan CRM fungerer
Funktioner i Apex-programmeringssproget
Her er de vigtige funktioner i Salesforce Apex:
- Apex er et sprog ufølsomt for store og små bogstaver.
- Du kan udføre DML-operationer som INSERT, UPDATE, UPSERT, DELETE på sObject-poster ved hjælp af apex.
- Du kan forespørge sObject-poster ved hjælp af SOQL(salesforce-objektforespørgselssprog) og SOSL(salesforce-objektsøgningssprog) i apex.
- Giver dig mulighed for at oprette en enhedstest og udfør dem for at verificere kodedækning og effektiviteten af koden i apex.
- Apex udføres i et miljø med flere lejere, og Salesforce har defineret nogle regulatorgrænser, der forhindrer en bruger i at kontrollere de delte ressourcer. Enhver kode, der krydser salgsstyrkens guvernørgrænse, mislykkes, en fejl dukker op.
- Salesforce-objekt kan bruges som en datatype i apex. For eksempel -
Account acc = new Account();
,her Account er et standard salesforce-objekt.
- Apex opgraderer automatisk med hver Salesforce-udgivelse.
Hvornår skal udvikleren vælge Apex
Apex-kode bør kun skrives, hvis et forretningsscenarie er for komplekst og ikke kan implementeres ved hjælp af den forudbyggede funktionalitet leveret af Salesforce.
Følgende er de få scenarier, hvor vi skal skrive apex-kode:
- At skabe webtjenester, der integrerer Salesforce med andre applikationer.
- At implementere tilpasset validering på sobjects.
- For at udføre brugerdefineret apex-logik, når en DML-handling udføres.
- At implementere funktionalitet, der ikke kan implementeres ved hjælp af eksisterende arbejdsgange flows og procesbyggerfunktionalitet.
- At oprette e -mail -tjenester, skal du inkludere behandling af indhold, overskrifter og vedhæftede filer i e-mail ved hjælp af apex-kode.
Arbejdsstruktur af Apex
Nu i denne Apex-tutorial lærer vi om Apex's arbejdsstruktur:
Følgende er flowet af handlinger for en apex-kode:
- Udviklerhandling: Al apex-koden skrevet af en udvikler kompileres i et sæt instruktioner, der kan forstås af apex runtime-fortolkeren, når udvikleren gemmer koden på platformen, og disse instruktioner derefter gemmer som metadata på platformen.
- Slutbrugerhandling: Når brugerhændelsen udfører en apex-kode, får platformsserveren de kompilerede instruktioner fra metadata og kører dem gennem apex-fortolkeren, før resultatet returneres.
Apex syntaks
Variabel erklæring
Da apex er et stærkt skrevet sprog, er det obligatorisk at deklarere en variabel med datatype i apex.
For eksempel:
contact con = new contact();
her er variablen con erklæret med kontakt som en datatype.
SOQL-forespørgsel
SOQL står for salesforce object query language. SOQL bruges til at hente sObject-poster fra Salesforce-databasen. For eksempel-
Account acc = [select id, name from Account Limit 1];
Ovenstående forespørgsel henter kontopost fra salesforce-databasen.
Løkkeerklæring
Loop statement bruges til at iterere over posterne på en liste. Antallet af iterationer er lig med antallet af poster på listen. For eksempel:
list<Account>listOfAccounts = [select id, name from account limit 100]; // iteration over the list of accounts for(Account acc : listOfAccounts){ //your logic }
I ovenstående kodestykke er listOfAccounts en variabel af listedatatypen.
Flowkontrolerklæring
Flowkontrolsætning er fordelagtig, når du ønsker at udføre nogle linjer i koden baseret på nogle betingelser.
For eksempel:
list<Account>listOfAccounts = [select id, name from account limit 100]; // execute the logic if the size of the account list is greater than zero if(listOfAccounts.size() >0){ //your logic }
Ovenstående kodestykke forespørger kontoposter fra databasen og kontrollerer listestørrelsen.
DML erklæring
DML står for datamanipulationssprog. DML-sætninger bruges til at manipulere data i Salesforce-databasen. For eksempel -
Account acc = new Account(Name = ‘ Test Account’); Insert acc; //DML statement to create account record.
Apex udviklingsmiljø
Nu i denne Apex-programmeringsvejledning lærer vi om Apex-udviklingsmiljøet:
Apex-kode kan udvikles enten i sandbox og udviklerudgave af Salesforce.
Det er en bedste praksis at udvikle koden i sandkassemiljøet og derefter implementere den til produktionsmiljøet.
Apex-kodeudviklingsværktøjer: Følgende er de tre tilgængelige værktøjer til at udvikle apex-kode i alle udgaver af Salesforce.
- Force.com Developer Console
- Force.com IDE
- Kodeeditor i Salesforce-brugergrænsefladenYou
Datatype i Apex
Følgende er de datatyper, der understøttes af apex:
Primitive
Heltal, Double, Long, Date, Date Time, String, ID og Boolean betragtes som primitive datatyper. Alle primitive datatyper videregives efter værdi, ikke ved reference.
Kollektioner
Tre typer samlinger er tilgængelige i Apex
- Liste: Det er en ordnet samling af primitiver, sObjects, samlinger eller Apex-objekter baseret på indekser.
- Sæt: En uordnet samling af unikke primitiver.
- Kort: Det er en samling af unikke, primitive nøgler, der knytter sig til enkelte værdier, som kan være primitiver, sObjects, samlinger eller Apex-objekter.
sObjekt
Dette er en speciel datatype i Salesforce. Det ligner et bord i SQL og indeholder felter, der ligner kolonner i SQL.
Enums
Enum er en abstrakt datatype, der gemmer én værdi af et endeligt sæt af specificerede identifikatorer
Klasser
Objekter
Det refererer til enhver datatype, der understøttes i Apex.
Interfaces
Apex Access Specifier
Følgende er adgangsspecifikationen, der understøttes af apex:
offentlige
Denne adgangsspecifikation giver adgang til en klasse, metode, variabel, der skal bruges af en apex i et navneområde.
Privat
Denne adgangsspecifikation giver adgang til en klasse, metode, variabel, der skal bruges lokalt eller inden for kodeafsnittet, det er defineret. Al teknikken, variabler, der ikke har nogen adgangsspecifikation defineret, har standardadgangsspecifikationen privat.
Beskyttet
Denne adgangsspecifikation giver adgang til en metode, variabel, der skal bruges af alle indre klasser inden for definerende Apex-klasse.
Global
Denne adgangsspecifikation giver adgang til en klasse, metode, variabel, der skal bruges af en apex inden for et navneområde såvel som uden for navneområdet. Det er en god praksis ikke at bruge globale søgeord, før det er nødvendigt.
Nøgleord i Apex
Med deling
Hvis en klasse er defineret med dette nøgleord, så håndhæves alle delingsreglerne for den aktuelle bruger, og hvis dette nøgleord er fraværende, udføres koden under systemkontekst.
For eksempel:
public with sharing class MyApexClass{ // sharing rules enforced when code in this class execute }
Uden at dele
Hvis en klasse er defineret med dette nøgleord, så håndhæves alle delingsreglerne for den aktuelle bruger ikke.
For eksempel:
public without sharing class MyApexClass{ // sharing rules is not enforced when code in this class execute }
statisk
En variabel, Metode er defineret med det statiske nøgleord initialiseres én gang og associeres med klassen. Statiske variabler, metoder kan kaldes ved klassenavn direkte uden at oprette forekomsten af en klasse.
Endelig
En konstant, Metode er defineret med det endelige søgeord kan ikke tilsidesættes. For eksempel:
public class myCls { static final Integer INT_CONST = 10; }
Hvis du forsøger at tilsidesætte værdien for denne INT_CONST variabel, vil du få en undtagelse – System.FinalException: Den endelige variabel er allerede blevet initialiseret.
Returnering
Dette søgeord returnerer en værdi fra en metode. For eksempel:
public String getName() { return 'Test' ; }
Null
Den definerer en nulkonstant og kan tildeles en variabel. For eksempel
Boolean b = null;
Virtual
Hvis en klasse er defineret med et virtuelt nøgleord, kan det udvides og tilsidesættes.
Abstrakt
Hvis en klasse er defineret med abstrakt nøgleord, skal den indeholde mindst én metode med nøgleord abstrakt, og den metode bør kun have en signatur.
For eksempel
public abstract class MyAbstrtactClass { abstract Integer myAbstractMethod1(); }
Apex streng
En streng er et sæt tegn uden tegnbegrænsninger. For eksempel:
String name = 'Test';
Der er flere indbyggede metoder leveret af String-klassen i salesforce. Følgende er de få ofte og mest brugte funktioner:
forkorte (maxWidth)
Denne metode afkorter en streng til den specificerede længde og returnerer den, hvis længden af den givne streng er længere end den specificerede længde; ellers returnerer den den originale streng. Hvis værdien for maxWidth variabel er mindre end 4, returnerer denne metode en runtime undtagelse – System.StringException: Minimum forkortelse width er 4
For eksempel:
String s = 'Hello World'; String s2 = s.abbreviate(8); System.debug('s2'+s2); //Hello...
kapitaliser ()
Denne metode konverterer det første bogstav i en streng til store og små bogstaver og returnerer det.
For eksempel:
String s = 'hello; String s2 = s.capitalize(); System.assertEquals('Hello', s2);
indeholder (understreng)
Denne metode returnerer sand, hvis den streng, der kalder metoden, indeholder den angivne understreng.
String name1 = 'test1'; String name2 = 'test2'; Boolean flag = name.contains(name2); System.debug('flag::',+flag); //true
er lig (stringOrId)
Denne metode returnerer sand, hvis parameteren, der sendes, ikke er null og angiver den samme binære sekvens af tegn som den streng, der kalder metoden.
Ved sammenligning af id-værdier er længden af id'erne muligvis ikke ens. For eksempel: Hvis en streng, der repræsenterer 15 tegn id sammenlignes med et objekt, der repræsenterer 18 tegn ID, returnerer denne metode sand. For eksempel:
Id idValue15 = '001D000000Ju1zH'; Id idValue18 = '001D000000Ju1zHIAR'; Boolean result4 = stringValue15.equals(IdValue18); System.debug('result4', +result4); //true
I ovenstående eksempel sammenligner metoden 15 tegn objekt Id med 18 tegn objekt Id, og hvis begge disse id repræsenterer den samme binære sekvens vil den returnere sand.
Brug denne metode til at foretage sammenligninger, der skelner mellem store og små bogstaver.
escapeSingle Quotes(stringToEscape)
Denne metode tilføjer et escape-tegn (\) før et enkelt citat i en streng og returnerer det. Denne metode forhindrer SOQL-injektion, mens du opretter en dynamisk SOQL-forespørgsel. Denne metode sikrer, at alle enkelte anførselstegn betragtes som omsluttende strenge i stedet for databasekommandoer.
For eksempel:
String s = 'Hello Tom'; system.debug(s); // Outputs 'Hello Tom' String escapedStr = String.escapeSingleQuotes(s); // Outputs \'Hello Tom\'
fjern (understreng)
Denne metode fjerner al forekomsten af den nævnte understreng fra den streng, der kalder metoden og returnerer den resulterende streng.
For eksempel
String s1 = 'Salesforce and force.com'; String s2 = s1.remove('force'); System.debug( 's2'+ s2);// 'Sales and .com'
understreng (startindeks)
Denne metode returnerer en understreng starter fra tegnet ved startIndex strækker sig til den sidste af strengen.
For eksempel:
String s1 = 'hamburger'; String s2 = s1.substring(3); System.debug('s2'+s2); //burger
baglæns()
Denne metode vender alle tegnene i en streng om og returnerer den. For eksempel:
String s = 'Hello'; String s2 = s.reverse(); System.debug('s2::::'+s2);// olleH // Hello
trim(): Denne metode fjerner alle de indledende hvide mellemrum fra en streng og returnerer den.
valueOf(toConvert)
Denne metode returnerer strengrepræsentationen af bestået i objekt.
Apex-guvernørgrænser
Apex-guvernørgrænser er de grænser, der håndhæves af apex-runtime-motoren for at sikre, at enhver runway-apex-kode og -processer ikke kontrollerer de delte ressourcer og ikke overtræder behandlingen for andre brugere i multitenant-miljøet. Disse grænser verificeres mod hver apex-transaktion. Følgende er guvernørgrænserne defineret af salesforce på hver apex-transaktion:
Description | Begræns |
---|---|
SOQL-forespørgsler, der kan udføres i en synkron transaktion | 100 |
SOQL-forespørgsler, der kan udføres i en asynkron transaktion | 200 |
Records, der kan hentes af en SOQL-forespørgsel | 50000 |
Records, der kan hentes af Database.getQueryLocator | 10000 |
SOSL-forespørgsler, der kan udføres i en apex-transaktion | 20 |
Records, der kan hentes ved en SOSL-forespørgsel | 2000 |
DML-udsagn, der kan udføres i en apex-transaktion | 150 |
Records, der kan behandles som et resultat af en DML-sætning, Approval.process eller database.emptyRecycleBin | 10000 |
Forklaringer, der kan udføres i en apex-transaktion. | 100 |
Kumulativ timeoutgrænse for alle de callouts, der udføres i en apex-transaktion | 120 sekunder |
Begrænsning af apex-job, der kan tilføjes til køen med System.enqueueJob | 50 |
Udførelsestidsgrænse for hver Apex-transaktion | 10 minutter |
Begræns for tegn, der kan bruges i en apex-klasse og trigger | 1 millioner |
CPU-tidsgrænse for synkron transaktion | 10,000 millisekunder |
CPU-tidsgrænse for asynkron transaktion | 60,000 millisekunder |
Apex Getter og Setter
Apex-egenskaben ligner apex-variabelen. Getter og setter er nødvendige for en apex-ejendom. Getter og setter kan bruges til at udføre kode, før ejendomsværdien tilgås eller ændres. Koden i get-accessoren udføres, når en egenskabsværdi læses. Koden i sæt-accessoren kører, når en egenskabsværdi ændres. Enhver ejendom, der har get accessor, betragtes som skrivebeskyttet, enhver ejendom, der har set accessor, anses for kun at skrive enhver ejendom, der har både get og set accessor, anses for at være read-write. Syntaks for en apex-egenskab:
public class myApexClass { // Property declaration access_modifierreturn_typeproperty_name { get { //code } set{ //code } }
Her er access_modifier egenskabens adgangsmodifikator. return_type er egenskabens dataType. ejendomsnavn er navnet på ejendommen.
Nedenfor er et eksempel på en apex-egenskab med både get- og set-accessor.
public class myApex{ public String name{ get{ return name;} set{ name = 'Test';} } }
Her er ejendomsnavnet navn, og det er offentlig ejendom, og det returnerer en streng dataType.
Det er ikke obligatorisk at have en eller anden kode i get and set-blokken. Disse blokke kan stå tomme for at definere en automatisk egenskab. For eksempel:
public double MyReadWriteProp{ get; set; }
Hent og indstil accessor kan også defineres med deres adgangsmodifikator. Hvis en accessor er defineret med en modifikator, så tilsidesætter den adgangsmodifikatoren for egenskaben. For eksempel:
public String name{private get; set;}// name is private for read and public to write.
Apex klasse
En apex-klasse er en blueprint eller skabelon, hvorfra objekter oprettes. Et objekt er instansen af en klasse.
Der er tre måder at oprette apex-klasser på i Salesforce:
Developer Console
Force.com IDE
Apex klasse detalje side.
I apex kan du definere en ydre klasse også kaldet top-level klasse, og du kan også definere klasser inden for en ydre klasse kaldet indre klasser.
Det er obligatorisk at bruge adgangsmodifikator som global eller offentlig i erklæringen om den ydre klasse.
Det er ikke nødvendigt at bruge adgangsmodifikator i erklæringen om indre klasser.
En apex-klasse defineres ved hjælp af klassenøgleord efterfulgt af klassenavnet.
Extends-nøgleord bruges til at udvide en eksisterende klasse med en apex-klasse, og implements-nøgleord bruges til at implementere en grænseflade med en apex-klasse.
Salesforce Apex understøtter ikke flere nedarvninger, en apex-klasse kan kun udvide én eksisterende apex-klasse, men kan implementere flere grænseflader.
En apex-klasse kan indeholde brugerdefineret konstruktør, og hvis en brugerdefineret konstruktør ikke er tilgængelig, bruges en standardkonstruktør. Koden i en konstruktør udføres, når en forekomst af en klasse oprettes.
Syntaks for Apex Class eksempel:
public class myApexClass{ // variable declaration //constructor public myApexClass{ } //methods declaration }
Det nye nøgleord bruges til at oprette en instans af en apex-klasse. Nedenfor er syntaksen til at oprette en forekomst af en apex-klasse.
myApexClass obj = new myApexClass();
Apex Trigger
Apex-triggere giver dig mulighed for at udføre tilpasset apex før og efter en DML-handling udføres.
Apex understøtter følgende to typer triggere:
Før triggere: Disse triggere bruges til at validere og opdatere feltets værdi, før posten gemmes i databasen.
Efter triggere: Disse triggere bruges til at få adgang til felterne (post-ID, LastModifiedDate-felt), der er angivet af systemet efter en post, der er forpligtet til databasen. Disse felters værdi kan bruges til at ændre andre poster. Registreringer, der udløses efter udløsere, er skrivebeskyttet.
Det er en god praksis at skrive omfangsrige udløsere. En omfangsrig trigger kan behandle en enkelt post såvel som flere poster ad gangen.
Syntaks for en apex-trigger:
trigger TriggerName on ObjectName (trigger_events) { //Code_block }
Her er TriggerName navnet på triggeren, ObjectName er navnet på det objekt, som triggeren skal skrives på, trigger_events er den kommaseparerede liste over hændelser.
Følgende er de hændelser, der understøttes af apex-triggerne: før indsættelse, før opdatering, før sletning, efter indsættelse, efter en opdatering, efter sletning, efter fortryd sletning.
Statiske søgeord kan ikke bruges i en Apex-udløser. Alle nøgleord, der gælder for indre klasser, kan bruges i en Apex-trigger.
Der er implicitte variable defineret af hver trigger, der returnerer runtime-konteksten. Disse variable er defineret i systemet. Trigger klasse. Disse variabler kaldes kontekstvariabler. Nedenstående skærmbillede viser kontekstvariablen understøttet af apex trigger.
Følgende er overvejelserne om kontekstvariablen i apex-triggeren:
- Brug ikke trigger.new og trigger.old i DML-operationer.
- Trigger.new kan ikke slettes.
- Trigger.new er skrivebeskyttet.
- Trigger.new kan kun bruges til at ændre værdierne af felterne på det samme objekt i før trigger.
Nedenstående skærmbilleder viser overvejelserne om specifikke handlinger i forskellige triggerhændelser.
Batch-klasse i Apex
Batch-klasse i salesforce bruges til at behandle et stort antal poster, der ville overskride apex-guvernørens grænser, hvis de behandles normalt. Batch-klassen udfører koden asynkront.
Følgende er fordelene ved batchklasse:
- Batch-klasse behandler dataene i bidder, og hvis en chunk ikke kan behandles med succes, ruller alle de chunks, der er behandlet med succes, ikke tilbage.
- Hver del af data i en batch-klasse behandles med et nyt sæt af regulatorgrænser, som sikrer, at koden udføres inden for regulatorens udførelsesgrænser.
- Database. Batchbar grænseflade skal implementeres af en apex-klasse for at blive brugt som en batch-klasse. Det giver tre metoder, som skal implementeres af batchklassen.
Følgende er de tre metoder, der leveres af Database. Batchbar grænseflade:
1.start()
Denne metode genererer omfanget af poster eller objekter, der skal behandles af interfacemetoden execute. Under udførelsen af batch kaldes det kun én gang. Denne metode returnerer enten et Database.QueryLocator-objekt eller en Iterable. Antallet af poster hentet af SQL-forespørgsel ved hjælp af Database.QueryLocator-objektet er 50 millioner poster, men ved brug af en iterable er det samlede antal poster, der kan hentes af SQL-forespørgslen, kun 50000. Iterable bruges til at generere komplekst omfang for batchklasse.
Syntaks for startmetode:
global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContextbc) {}
2.execute()
Denne metode bruges til at behandle hver del af data. For hver chunk af poster kaldes executive-metoden. Standard batchstørrelse for udførelse er 200 poster. Udfør metode tager to argumenter:
En reference til Database.BatchableContext-objektet,
En liste over sObjects, såsom List , eller en liste over parametriserede typer. Syntaks for udførelsesmetode:
global void execute(Database.BatchableContext BC, list<P>){}
3.finish()
Afslutningsmetoden kaldes én gang under udførelsen af batchklassen. Efterbehandlingsoperationer kan udføres i finishmetoden. For eksempel: at sende bekræftelsesmailen. Denne metode kaldes, når hele batchen bliver behandlet. Syntaks for Finish-metoden:
global void finish(Database.BatchableContext BC){}
Database.BatchableContext objekt
Hver metode i databasen. Batchable interface har en reference til Database.BatchableContext objekt.
Dette objekt bruges til at spore batchjobbets fremskridt.
Følgende er instansmetoder leveret af BatchableContext:
- getChildJobId(): Denne metode returnerer ID'et for et batchjob, der i øjeblikket behandles.
- getJobId(): Denne metode returnerer batchjobbets ID.
Nedenfor er syntaksen for en batchklasse:
global class MyBatchClass implements Database.Batchable<sObject> { global (Database.QueryLocator | Iterable<sObject>) start(Database.BatchableContextbc) { // collect the batches of records or objects to be passed to execute } global void execute(Database.BatchableContextbc, List<P> records){ // process each batch of records } global void finish(Database.BatchableContextbc){ // execute any post-processing operations } }
Database.executeBatch-metode
Database.executeBatch-metoden bruges til at udføre en batch-klasse.
Denne metode tager to parametre: Forekomst af batchklassen, der skal behandles, Options-parameteren for at angive batchstørrelsen, hvis den ikke er angivet, den tager standardstørrelsen på 200.
Syntaks for Database.executeBatch:
Database.executeBatch(myBatchObject,scope)
Udførelse af et batch-klassenavn MyBatchClass:
MyBatchClassmyBatchObject = new MyBatchClass(); Id batchId = Database.executeBatch(myBatchObject,100);
Database.stateful
Batch-klassen er som standard statsløs. Hver gang execute-metoden kaldes en ny kopi af et objekt modtages, initialiseres alle variabler i klassen.
Database.stateful er implementeret for at gøre en batch-klasse stateful.
Hvis din batch-klasse implementerede Database , stateful interface beholder alle instansvariabler deres værdier, men de statiske variable nulstilles mellem transaktionen.
Resumé
- Apex er en stærkt indtastet, objektorienteret programmeringssprog der kompilerer og kører på force.com-platformen
- Apex programmeringssprog er et sprog ufølsomt for store og små bogstaver
- To typer handlingsflow i Apex er 1) Udviklerhandling 2) Slutbrugerhandling
- Apex hjælper dig med at skabe webtjenester, der integrerer Salesforce med andre applikationer.
- Datatyper understøttet af apex er: 1). Primitiv 2) Samlinger 3) sObject, Enums, 4) Klasser, 5) Objekter og grænseflader
- Offentlig, privat, beskyttet og global er specificeret support af Apex
- Nøgleord, der bruges i Apex er: 1) Med deling, 2) Uden deling, 3) Statisk, 4) Endelig 5) Retur, 6) Nul, 7) Virtuel, 8) Abstrakt
- En streng er et sæt tegn uden tegnbegrænsninger
- Apex-guvernørgrænser er de grænser, der håndhæves af apex-runtime-motoren for at sikre, at enhver startbane-apex-kode og -processer
- Getter og setter kan bruges til at udføre kode, før ejendomsværdien tilgås eller ændres
- Der er tre måder at oprette apex-klasser på i Salesforce: 1)Udviklerkonsol 2)Force.com IDE og 3) Apex-klassedetaljeside.
- Apex-triggere giver dig mulighed for at udføre tilpasset apex før og efter en DML-handling udføres.
- Batch-klasse i salesforce bruges til at behandle et stort antal poster, der ville overskride apex-guvernørens grænser, hvis de behandles normalt.